From dc5151b81adcfc65dec90c893314a8e7d02637da Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Fri, 13 Dec 2013 12:32:26 -0500 Subject: [PATCH] fix GeoJSON roundtripping --- build/deps.js | 2 +- debug/vector/vector2.html | 2 +- spec/suites/layer/GeoJSONSpec.js | 12 +-- src/layer/GeoJSON.js | 160 +++++++++++++++---------------- src/layer/vector/Polyline.js | 6 +- 5 files changed, 88 insertions(+), 94 deletions(-) diff --git a/build/deps.js b/build/deps.js index 0be0ba33..54179c04 100644 --- a/build/deps.js +++ b/build/deps.js @@ -147,7 +147,7 @@ var deps = { GeoJSON: { src: ['layer/GeoJSON.js'], - deps: ['Polygon', 'Circle', 'Marker', 'FeatureGroup'], + deps: ['Polygon', 'Circle', 'CircleMarker', 'Marker', 'FeatureGroup'], desc: 'GeoJSON layer, parses the data and adds corresponding layers above.' }, diff --git a/debug/vector/vector2.html b/debug/vector/vector2.html index 779fa95c..e51dfd94 100644 --- a/debug/vector/vector2.html +++ b/debug/vector/vector2.html @@ -36,7 +36,7 @@ var circle = L.circle([35, 0], 700000, {color: 'green', renderer: canvas}).addTo(map).bindPopup('Hello Circle'); - map.fitBounds(path); + map.setView([36, 52], 3); diff --git a/spec/suites/layer/GeoJSONSpec.js b/spec/suites/layer/GeoJSONSpec.js index 0f326dd8..ec02fbf5 100644 --- a/spec/suites/layer/GeoJSONSpec.js +++ b/spec/suites/layer/GeoJSONSpec.js @@ -95,9 +95,9 @@ describe("L.Polyline#toGeoJSON", function () { }); }); -describe("L.MultiPolyline#toGeoJSON", function () { +describe("L.Polyline (multi) #toGeoJSON", function () { it("returns a 2D MultiLineString object", function () { - var multiPolyline = new L.MultiPolyline([[[10, 20], [2, 5]], [[1, 2], [3, 4]]]); + var multiPolyline = new L.Polyline([[[10, 20], [2, 5]], [[1, 2], [3, 4]]]); expect(multiPolyline.toGeoJSON().geometry).to.eql({ type: 'MultiLineString', coordinates: [ @@ -108,7 +108,7 @@ describe("L.MultiPolyline#toGeoJSON", function () { }); it("returns a 3D MultiLineString object", function () { - var multiPolyline = new L.MultiPolyline([[[10, 20, 30], [2, 5, 10]], [[1, 2, 3], [4, 5, 6]]]); + var multiPolyline = new L.Polyline([[[10, 20, 30], [2, 5, 10]], [[1, 2, 3], [4, 5, 6]]]); expect(multiPolyline.toGeoJSON().geometry).to.eql({ type: 'MultiLineString', coordinates: [ @@ -159,9 +159,9 @@ describe("L.Polygon#toGeoJSON", function () { }); }); -describe("L.MultiPolygon#toGeoJSON", function () { +describe("L.Polygon (multi) #toGeoJSON", function () { it("returns a 2D MultiPolygon object", function () { - var multiPolygon = new L.MultiPolygon([[[1, 2], [3, 4], [5, 6]]]); + var multiPolygon = new L.Polygon([[[1, 2], [3, 4], [5, 6]]]); expect(multiPolygon.toGeoJSON().geometry).to.eql({ type: 'MultiPolygon', coordinates: [ @@ -171,7 +171,7 @@ describe("L.MultiPolygon#toGeoJSON", function () { }); it("returns a 3D MultiPolygon object", function () { - var multiPolygon = new L.MultiPolygon([[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]); + var multiPolygon = new L.Polygon([[[1, 2, 3], [4, 5, 6], [7, 8, 9]]]); expect(multiPolygon.toGeoJSON().geometry).to.eql({ type: 'MultiPolygon', coordinates: [ diff --git a/src/layer/GeoJSON.js b/src/layer/GeoJSON.js index c70fd62f..fe3a457d 100644 --- a/src/layer/GeoJSON.js +++ b/src/layer/GeoJSON.js @@ -99,23 +99,14 @@ L.extend(L.GeoJSON, { return new L.FeatureGroup(layers); case 'LineString': - latlngs = this.coordsToLatLngs(coords, 0, coordsToLatLng); + case 'MultiLineString': + latlngs = this.coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, coordsToLatLng); return new L.Polyline(latlngs, options); case 'Polygon': - if (coords.length === 2 && !coords[1].length) { - throw new Error('Invalid GeoJSON object.'); - } - latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng); - return new L.Polygon(latlngs, options); - - case 'MultiLineString': - latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng); - return new L.MultiPolyline(latlngs, options); - case 'MultiPolygon': - latlngs = this.coordsToLatLngs(coords, 2, coordsToLatLng); - return new L.MultiPolygon(latlngs, options); + latlngs = this.coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, coordsToLatLng); + return new L.Polygon(latlngs, options); case 'GeometryCollection': for (i = 0, len = geometry.geometries.length; i < len; i++) { @@ -157,11 +148,17 @@ L.extend(L.GeoJSON, { [latlng.lng, latlng.lat]; }, - latLngsToCoords: function (latLngs) { + latLngsToCoords: function (latlngs, levelsDeep, closed) { var coords = []; - for (var i = 0, len = latLngs.length; i < len; i++) { - coords.push(L.GeoJSON.latLngToCoords(latLngs[i])); + for (var i = 0, len = latlngs.length; i < len; i++) { + coords.push(levelsDeep ? + L.GeoJSON.latLngsToCoords(latlngs[i], levelsDeep - 1, closed): + L.GeoJSON.latLngToCoords(latlngs[i])); + } + + if (!levelsDeep && closed) { + coords.push(coords[0]); } return coords; @@ -199,90 +196,83 @@ L.Marker.include(PointToGeoJSON); L.Circle.include(PointToGeoJSON); L.CircleMarker.include(PointToGeoJSON); -L.Polyline.include({ - toGeoJSON: function () { - return L.GeoJSON.getFeature(this, { - type: 'LineString', - coordinates: L.GeoJSON.latLngsToCoords(this.getLatLngs()) - }); +L.Polyline.prototype.toGeoJSON = function () { + var multi = !this._flat(this._latlngs); + + var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 1 : 0); + + return L.GeoJSON.getFeature(this, { + type: (multi ? 'Multi' : '') + 'LineString', + coordinates: coords + }); +}; + +L.Polygon.prototype.toGeoJSON = function () { + var holes = !this._flat(this._latlngs), + multi = holes && !this._flat(this._latlngs[0]); + + var coords = L.GeoJSON.latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true); + + if (holes && this._latlngs.length === 1) { + multi = true; + coords = [coords]; + } + if (!holes) { + coords = [coords]; } -}); -L.Polygon.include({ - toGeoJSON: function () { - var coords = [L.GeoJSON.latLngsToCoords(this.getLatLngs())], - i, len, hole; + return L.GeoJSON.getFeature(this, { + type: (multi ? 'Multi' : '') + 'Polygon', + coordinates: coords + }); +}; - coords[0].push(coords[0][0]); - if (this._holes) { - for (i = 0, len = this._holes.length; i < len; i++) { - hole = L.GeoJSON.latLngsToCoords(this._holes[i]); - hole.push(hole[0]); - coords.push(hole); - } - } +L.LayerGroup.include({ + toMultiPoint: function () { + var coords = []; + + this.eachLayer(function (layer) { + coords.push(layer.toGeoJSON().geometry.coordinates); + }); return L.GeoJSON.getFeature(this, { - type: 'Polygon', + type: 'MultiPoint', coordinates: coords }); - } -}); + }, -(function () { - function multiToGeoJSON(type) { - return function () { - var coords = []; + toGeoJSON: function () { - this.eachLayer(function (layer) { - coords.push(layer.toGeoJSON().geometry.coordinates); - }); + var type = this.feature && this.feature.geometry && this.feature.geometry.type; + if (type === 'MultiPoint') { + return this.toMultiPoint(); + } + + var isGeometryCollection = type === 'GeometryCollection', + jsons = []; + + this.eachLayer(function (layer) { + if (layer.toGeoJSON) { + var json = layer.toGeoJSON(); + jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json)); + } + }); + + if (isGeometryCollection) { return L.GeoJSON.getFeature(this, { - type: type, - coordinates: coords + geometries: jsons, + type: 'GeometryCollection' }); + } + + return { + type: 'FeatureCollection', + features: jsons }; } - - L.MultiPolyline.include({toGeoJSON: multiToGeoJSON('MultiLineString')}); - L.MultiPolygon.include({toGeoJSON: multiToGeoJSON('MultiPolygon')}); - - L.LayerGroup.include({ - toGeoJSON: function () { - - var geometry = this.feature && this.feature.geometry, - jsons = [], - json; - - if (geometry && geometry.type === 'MultiPoint') { - return multiToGeoJSON('MultiPoint').call(this); - } - - var isGeometryCollection = geometry && geometry.type === 'GeometryCollection'; - - this.eachLayer(function (layer) { - if (layer.toGeoJSON) { - json = layer.toGeoJSON(); - jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json)); - } - }); - - if (isGeometryCollection) { - return L.GeoJSON.getFeature(this, { - geometries: jsons, - type: 'GeometryCollection' - }); - } - - return { - type: 'FeatureCollection', - features: jsons - }; - } - }); -}()); +}); L.geoJson = function (geojson, options) { return new L.GeoJSON(geojson, options); diff --git a/src/layer/vector/Polyline.js b/src/layer/vector/Polyline.js index 79f2c902..c295f298 100644 --- a/src/layer/vector/Polyline.js +++ b/src/layer/vector/Polyline.js @@ -72,7 +72,7 @@ L.Polyline = L.Path.extend({ _convertLatLngs: function (latlngs) { var result = [], - flat = !L.Util.isArray(latlngs[0]) || typeof latlngs[0][0] === 'number'; + flat = this._flat(latlngs); for (var i = 0, len = latlngs.length; i < len; i++) { result[i] = flat ? L.latLng(latlngs[i]) : this._convertLatLngs(latlngs[i]); @@ -81,6 +81,10 @@ L.Polyline = L.Path.extend({ return result; }, + _flat: function (latlngs) { + return !L.Util.isArray(latlngs[0]) || typeof latlngs[0][0] === 'number'; + }, + _project: function () { this._rings = []; this._projectLatlngs(this._latlngs, this._rings);