|
| 1 | +(function (factory) { |
| 2 | + var L, proj4; |
| 3 | + if (typeof define === 'function' && define.amd) { |
| 4 | + // AMD |
| 5 | + define(['leaflet', 'proj4'], factory); |
| 6 | + } else if (typeof module === 'object' && typeof module.exports === "object") { |
| 7 | + // Node/CommonJS |
| 8 | + L = require('leaflet'); |
| 9 | + proj4 = require('proj4'); |
| 10 | + module.exports = factory(L, proj4); |
| 11 | + } else { |
| 12 | + // Browser globals |
| 13 | + if (typeof window.L === 'undefined' || typeof window.proj4 === 'undefined') |
| 14 | + throw 'Leaflet and proj4 must be loaded first'; |
| 15 | + factory(window.L, window.proj4); |
| 16 | + } |
| 17 | +}(function (L, proj4) { |
| 18 | + if (proj4.__esModule && proj4.default) { |
| 19 | + // If proj4 was bundled as an ES6 module, unwrap it to get |
| 20 | + // to the actual main proj4 object. |
| 21 | + // See discussion in https://github.com/kartena/Proj4Leaflet/pull/147 |
| 22 | + proj4 = proj4.default; |
| 23 | + } |
| 24 | + |
| 25 | + L.Proj = {}; |
| 26 | + |
| 27 | + L.Proj._isProj4Obj = function(a) { |
| 28 | + return (typeof a.inverse !== 'undefined' && |
| 29 | + typeof a.forward !== 'undefined'); |
| 30 | + }; |
| 31 | + |
| 32 | + L.Proj.Projection = L.Class.extend({ |
| 33 | + initialize: function(code, def, bounds) { |
| 34 | + var isP4 = L.Proj._isProj4Obj(code); |
| 35 | + this._proj = isP4 ? code : this._projFromCodeDef(code, def); |
| 36 | + this.bounds = isP4 ? def : bounds; |
| 37 | + }, |
| 38 | + |
| 39 | + project: function (latlng) { |
| 40 | + var point = this._proj.forward([latlng.lng, latlng.lat]); |
| 41 | + return new L.Point(point[0], point[1]); |
| 42 | + }, |
| 43 | + |
| 44 | + unproject: function (point, unbounded) { |
| 45 | + var point2 = this._proj.inverse([point.x, point.y]); |
| 46 | + return new L.LatLng(point2[1], point2[0], unbounded); |
| 47 | + }, |
| 48 | + |
| 49 | + _projFromCodeDef: function(code, def) { |
| 50 | + if (def) { |
| 51 | + proj4.defs(code, def); |
| 52 | + } else if (proj4.defs[code] === undefined) { |
| 53 | + var urn = code.split(':'); |
| 54 | + if (urn.length > 3) { |
| 55 | + code = urn[urn.length - 3] + ':' + urn[urn.length - 1]; |
| 56 | + } |
| 57 | + if (proj4.defs[code] === undefined) { |
| 58 | + throw 'No projection definition for code ' + code; |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + return proj4(code); |
| 63 | + } |
| 64 | + }); |
| 65 | + |
| 66 | + L.Proj.CRS = L.Class.extend({ |
| 67 | + includes: L.CRS, |
| 68 | + |
| 69 | + options: { |
| 70 | + transformation: new L.Transformation(1, 0, -1, 0) |
| 71 | + }, |
| 72 | + |
| 73 | + initialize: function(a, b, c) { |
| 74 | + var code, |
| 75 | + proj, |
| 76 | + def, |
| 77 | + options; |
| 78 | + |
| 79 | + if (L.Proj._isProj4Obj(a)) { |
| 80 | + proj = a; |
| 81 | + code = proj.srsCode; |
| 82 | + options = b || {}; |
| 83 | + |
| 84 | + this.projection = new L.Proj.Projection(proj, options.bounds); |
| 85 | + } else { |
| 86 | + code = a; |
| 87 | + def = b; |
| 88 | + options = c || {}; |
| 89 | + this.projection = new L.Proj.Projection(code, def, options.bounds); |
| 90 | + } |
| 91 | + |
| 92 | + L.Util.setOptions(this, options); |
| 93 | + this.code = code; |
| 94 | + this.transformation = this.options.transformation; |
| 95 | + |
| 96 | + if (this.options.origin) { |
| 97 | + this.transformation = |
| 98 | + new L.Transformation(1, -this.options.origin[0], |
| 99 | + -1, this.options.origin[1]); |
| 100 | + } |
| 101 | + |
| 102 | + if (this.options.scales) { |
| 103 | + this._scales = this.options.scales; |
| 104 | + } else if (this.options.resolutions) { |
| 105 | + this._scales = []; |
| 106 | + for (var i = this.options.resolutions.length - 1; i >= 0; i--) { |
| 107 | + if (this.options.resolutions[i]) { |
| 108 | + this._scales[i] = 1 / this.options.resolutions[i]; |
| 109 | + } |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + this.infinite = !this.options.bounds; |
| 114 | + |
| 115 | + }, |
| 116 | + |
| 117 | + scale: function(zoom) { |
| 118 | + var iZoom = Math.floor(zoom), |
| 119 | + baseScale, |
| 120 | + nextScale, |
| 121 | + scaleDiff, |
| 122 | + zDiff; |
| 123 | + if (zoom === iZoom) { |
| 124 | + return this._scales[zoom]; |
| 125 | + } else { |
| 126 | + // Non-integer zoom, interpolate |
| 127 | + baseScale = this._scales[iZoom]; |
| 128 | + nextScale = this._scales[iZoom + 1]; |
| 129 | + scaleDiff = nextScale - baseScale; |
| 130 | + zDiff = (zoom - iZoom); |
| 131 | + return baseScale + scaleDiff * zDiff; |
| 132 | + } |
| 133 | + }, |
| 134 | + |
| 135 | + zoom: function(scale) { |
| 136 | + // Find closest number in this._scales, down |
| 137 | + var downScale = this._closestElement(this._scales, scale), |
| 138 | + downZoom = this._scales.indexOf(downScale), |
| 139 | + nextScale, |
| 140 | + nextZoom, |
| 141 | + scaleDiff; |
| 142 | + // Check if scale is downScale => return array index |
| 143 | + if (scale === downScale) { |
| 144 | + return downZoom; |
| 145 | + } |
| 146 | + if (downScale === undefined) { |
| 147 | + return -Infinity; |
| 148 | + } |
| 149 | + // Interpolate |
| 150 | + nextZoom = downZoom + 1; |
| 151 | + nextScale = this._scales[nextZoom]; |
| 152 | + if (nextScale === undefined) { |
| 153 | + return Infinity; |
| 154 | + } |
| 155 | + scaleDiff = nextScale - downScale; |
| 156 | + return (scale - downScale) / scaleDiff + downZoom; |
| 157 | + }, |
| 158 | + |
| 159 | + distance: L.CRS.Earth.distance, |
| 160 | + |
| 161 | + R: L.CRS.Earth.R, |
| 162 | + |
| 163 | + /* Get the closest lowest element in an array */ |
| 164 | + _closestElement: function(array, element) { |
| 165 | + var low; |
| 166 | + for (var i = array.length; i--;) { |
| 167 | + if (array[i] <= element && (low === undefined || low < array[i])) { |
| 168 | + low = array[i]; |
| 169 | + } |
| 170 | + } |
| 171 | + return low; |
| 172 | + } |
| 173 | + }); |
| 174 | + |
| 175 | + L.Proj.GeoJSON = L.GeoJSON.extend({ |
| 176 | + initialize: function(geojson, options) { |
| 177 | + this._callLevel = 0; |
| 178 | + L.GeoJSON.prototype.initialize.call(this, geojson, options); |
| 179 | + }, |
| 180 | + |
| 181 | + addData: function(geojson) { |
| 182 | + var crs; |
| 183 | + |
| 184 | + if (geojson) { |
| 185 | + if (geojson.crs && geojson.crs.type === 'name') { |
| 186 | + crs = new L.Proj.CRS(geojson.crs.properties.name); |
| 187 | + } else if (geojson.crs && geojson.crs.type) { |
| 188 | + crs = new L.Proj.CRS(geojson.crs.type + ':' + geojson.crs.properties.code); |
| 189 | + } |
| 190 | + |
| 191 | + if (crs !== undefined) { |
| 192 | + this.options.coordsToLatLng = function(coords) { |
| 193 | + var point = L.point(coords[0], coords[1]); |
| 194 | + return crs.projection.unproject(point); |
| 195 | + }; |
| 196 | + } |
| 197 | + } |
| 198 | + |
| 199 | + // Base class' addData might call us recursively, but |
| 200 | + // CRS shouldn't be cleared in that case, since CRS applies |
| 201 | + // to the whole GeoJSON, inluding sub-features. |
| 202 | + this._callLevel++; |
| 203 | + try { |
| 204 | + L.GeoJSON.prototype.addData.call(this, geojson); |
| 205 | + } finally { |
| 206 | + this._callLevel--; |
| 207 | + if (this._callLevel === 0) { |
| 208 | + delete this.options.coordsToLatLng; |
| 209 | + } |
| 210 | + } |
| 211 | + } |
| 212 | + }); |
| 213 | + |
| 214 | + L.Proj.geoJson = function(geojson, options) { |
| 215 | + return new L.Proj.GeoJSON(geojson, options); |
| 216 | + }; |
| 217 | + |
| 218 | + L.Proj.ImageOverlay = L.ImageOverlay.extend({ |
| 219 | + initialize: function (url, bounds, options) { |
| 220 | + L.ImageOverlay.prototype.initialize.call(this, url, null, options); |
| 221 | + this._projectedBounds = bounds; |
| 222 | + }, |
| 223 | + |
| 224 | + // Danger ahead: Overriding internal methods in Leaflet. |
| 225 | + // Decided to do this rather than making a copy of L.ImageOverlay |
| 226 | + // and doing very tiny modifications to it. |
| 227 | + // Future will tell if this was wise or not. |
| 228 | + _animateZoom: function (event) { |
| 229 | + var scale = this._map.getZoomScale(event.zoom); |
| 230 | + var northWest = L.point(this._projectedBounds.min.x, this._projectedBounds.max.y); |
| 231 | + var offset = this._projectedToNewLayerPoint(northWest, event.zoom, event.center); |
| 232 | + |
| 233 | + L.DomUtil.setTransform(this._image, offset, scale); |
| 234 | + }, |
| 235 | + |
| 236 | + _reset: function () { |
| 237 | + var zoom = this._map.getZoom(); |
| 238 | + var pixelOrigin = this._map.getPixelOrigin(); |
| 239 | + var bounds = L.bounds( |
| 240 | + this._transform(this._projectedBounds.min, zoom)._subtract(pixelOrigin), |
| 241 | + this._transform(this._projectedBounds.max, zoom)._subtract(pixelOrigin) |
| 242 | + ); |
| 243 | + var size = bounds.getSize(); |
| 244 | + |
| 245 | + L.DomUtil.setPosition(this._image, bounds.min); |
| 246 | + this._image.style.width = size.x + 'px'; |
| 247 | + this._image.style.height = size.y + 'px'; |
| 248 | + }, |
| 249 | + |
| 250 | + _projectedToNewLayerPoint: function (point, zoom, center) { |
| 251 | + var viewHalf = this._map.getSize()._divideBy(2); |
| 252 | + var newTopLeft = this._map.project(center, zoom)._subtract(viewHalf)._round(); |
| 253 | + var topLeft = newTopLeft.add(this._map._getMapPanePos()); |
| 254 | + |
| 255 | + return this._transform(point, zoom)._subtract(topLeft); |
| 256 | + }, |
| 257 | + |
| 258 | + _transform: function (point, zoom) { |
| 259 | + var crs = this._map.options.crs; |
| 260 | + var transformation = crs.transformation; |
| 261 | + var scale = crs.scale(zoom); |
| 262 | + |
| 263 | + return transformation.transform(point, scale); |
| 264 | + } |
| 265 | + }); |
| 266 | + |
| 267 | + L.Proj.imageOverlay = function (url, bounds, options) { |
| 268 | + return new L.Proj.ImageOverlay(url, bounds, options); |
| 269 | + }; |
| 270 | + |
| 271 | + return L.Proj; |
| 272 | +})); |
0 commit comments