diff --git a/maps_utils/const.py b/maps_utils/const.py index e7c33027e0183c018da42fd31aefaed060c09fe2..1129ac93d8500aa34e7f0889c2637f3d96f54958 100644 --- a/maps_utils/const.py +++ b/maps_utils/const.py @@ -9,4 +9,11 @@ MAP_STYLES = ( DEFAULT_MAP_STYLE = "osm-mapnik" -SUPPORTED_FEATURE_TYPES = ("Point", "Polygon", "LineString") +SUPPORTED_FEATURE_TYPES = ( + "Point", + "MultiPoint", + "Polygon", + "MultiPolygon", + "LineString", + "MultiLineString", +) diff --git a/maps_utils/static/maps_utils/geo-feature-collection.js b/maps_utils/static/maps_utils/geo-feature-collection.js index 8f5265167999e3e7a8055018af26e1d83380bd29..f974dbe7cf6f3a3e32c3a7eebfc5247142e362fe 100644 --- a/maps_utils/static/maps_utils/geo-feature-collection.js +++ b/maps_utils/static/maps_utils/geo-feature-collection.js @@ -253,16 +253,7 @@ const GeoFeatureCollection = Vue.component("GeoFeatureCollection", { ) : buildMarkerIcon("000000", number); - /** - * Process Point type GeoJSON features. - * Called for each such feature when building the map. - */ - const pointToLayer = (feature, latlng) => { - const markerPosLatLng = L.latLng( - feature.geometry.coordinates[1], - feature.geometry.coordinates[0] - ); - + const addMarker = (feature, markerPosLatLng, onClick) => { // add marker const featureMarker = new L.marker(markerPosLatLng, { icon: feature.properties.color @@ -274,72 +265,83 @@ const GeoFeatureCollection = Vue.component("GeoFeatureCollection", { feature.properties.category, feature.properties.index ), - }).on("click", (evt) => { - this.zoomToPoint(evt.latlng, feature); - }); + }).on("click", onClick); // Add item marker to the cluster. markers.addLayer(featureMarker); - return false; + return featureMarker; + } + + /** + * Process Point/MultiPoint type GeoJSON features. + * Called for each such feature when building the map. + */ + const pointToLayer = (feature, latlng) => { + const onClick = (evt) => { + this.zoomToPoint(evt.latlng, feature); + }; + return addMarker(feature, latlng, onClick); }; /** - * Process Polygon/LineString type GeoJSON features. + * Process Polygon/MultiPolygon/LineString/MultiLineString type GeoJSON features. * Called for each such feature when building the map. */ const onEachFeature = (feature, layer) => { - let markerPosLatLng; + const markerPosLatLng = []; + const onClick = (evt) => this.zoomToLayer(layer); - /** - * Supported GeoJSON features: Polygon, LineString. - * Point features are better handled by pointToLayer function. - * It's better idea to convert Points to small Polygons (ask marek.forster@pirati.cz for conversion tool) - **/ - // Polygons and LineString features are supported, try to get rid of Points (bounds and zoom methods not supported) - if (feature.geometry.type == "Polygon") { + const markerForPolyCoords = (coords) => { // Find pole of inaccessibility (not centroid) for the polygon // @see: https://github.com/mapbox/polylabel - const markerPos = polylabel( - feature.geometry.coordinates, - 1 - ); - markerPosLatLng = L.latLng(markerPos[1], markerPos[0]); - } else if (feature.geometry.type == "LineString") { + const markerPos = polylabel(coords, 1); + markerPosLatLng.push(L.latLng(markerPos[1], markerPos[0])); + }; + + const markerForLineStringCoords = (coords) => { // Find a middle sector of LineString and set position to middle of it - const sectorCount = feature.geometry.coordinates.length; + const sectorCount = coords.length; const sectorIndex = Math.floor((sectorCount - 1) / 2); - markerPosLatLng = L.latLng( - (feature.geometry.coordinates[sectorIndex][1] + - feature.geometry.coordinates[sectorIndex + 1][1]) / + + markerPosLatLng.push(L.latLng( + (coords[sectorIndex][1] + + coords[sectorIndex + 1][1]) / 2, - (feature.geometry.coordinates[sectorIndex][0] + - feature.geometry.coordinates[sectorIndex + 1][0]) / + (coords[sectorIndex][0] + + coords[sectorIndex + 1][0]) / 2 - ); + )); + } + + /** + * Supported GeoJSON features: Polygon, MultiPolygon, LineString, MultiLineString. + * Point/MultiPoint features are better handled by pointToLayer function. + * It's better idea to convert Points to small Polygons (ask marek.forster@pirati.cz for conversion tool) as + * bounds and zoom methods are not supported for those. + **/ + if (feature.geometry.type == "Polygon") { + markerForPolyCoords(feature.geometry.coordinates); + } else if (feature.geometry.type == "MultiPolygon") { + feature.geometry.coordinates.forEach(markerForPolyCoords); + } else if (feature.geometry.type == "LineString") { + markerForLineStringCoords(feature.geometry.coordinates); + } else if (feature.geometry.type == "MultiLineString") { + feature.geometry.coordinates.forEach(markerForLineStringCoords); + } else if (feature.geometry.type == "MultiPoint") { + // Supported via `pointToLayer`, noop here. } else { console.warn( `GeoJSON feature type unsupported: ${feature.geometry.type}` ); } - if (markerPosLatLng) { - // add marker - const featureMarker = new L.marker(markerPosLatLng, { - icon: feature.properties.color - ? buildMarkerIcon( - feature.properties.color, - feature.properties.index - ) - : markerIconForCategory( - feature.properties.category, - feature.properties.index - ), - }).on("click", (evt) => this.zoomToLayer(layer)); - // Add item marker to the cluster. - markers.addLayer(featureMarker); - // Bind click event on the layer. - layer.on({ click: (evt) => this.zoomToLayer(evt.target) }); + if (markerPosLatLng.length) { + markerPosLatLng.forEach(pos => { + addMarker(feature, pos, onClick); + // Bind click event on the layer. + layer.on({ click: (evt) => this.zoomToLayer(evt.target) }); + }) } };