From 4359652d20996ebd10a3410ec354f94865704b70 Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Thu, 1 Dec 2016 14:43:19 +0100 Subject: [PATCH 1/6] Fix #1314 Support to print vector layer (leaflet) - Support vector style for leaflet layers - Conversion to OL2 style format - add reprojectGeoJson utility method to CoordinateUtils Missing: - Print preview support - Markers --- web/client/utils/CoordinatesUtils.js | 92 +++++++++++++++- web/client/utils/PrintUtils.js | 103 ++++++++++++++++++ .../utils/__tests__/CoordinatesUtils-test.js | 29 +++++ web/client/utils/__tests__/PrintUtils-test.js | 92 +++++++++++++++- web/client/utils/openlayers/StyleUtils.js | 8 ++ 5 files changed, 319 insertions(+), 5 deletions(-) diff --git a/web/client/utils/CoordinatesUtils.js b/web/client/utils/CoordinatesUtils.js index c5ecbab537..99d1e2e231 100644 --- a/web/client/utils/CoordinatesUtils.js +++ b/web/client/utils/CoordinatesUtils.js @@ -5,11 +5,49 @@ * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ -var Proj4js = require('proj4'); -var assign = require('object-assign'); -var {isArray, flattenDeep, chunk} = require('lodash'); +const Proj4js = require('proj4'); +const proj4 = Proj4js; +const assign = require('object-assign'); +const {isArray, flattenDeep, chunk, cloneDeep} = require('lodash'); +// Checks if `list` looks like a `[x, y]`. +function isXY(list) { + return list.length >= 2 && + typeof list[0] === 'number' && + typeof list[1] === 'number'; +} +function traverseCoords(coordinates, callback) { + if (isXY(coordinates)) return callback(coordinates); + return coordinates.map(function(coord) { return traverseCoords(coord, callback); }); +} -var CoordinatesUtils = { +function traverseGeoJson(geojson, leafCallback, nodeCallback) { + if (geojson === null) return geojson; + + let r = cloneDeep(geojson); + + if (geojson.type === 'Feature') { + r.geometry = traverseGeoJson(geojson.geometry, leafCallback, nodeCallback); + } else if (geojson.type === 'FeatureCollection') { + r.features = r.features.map(function(gj) { return traverseGeoJson(gj, leafCallback, nodeCallback); }); + } else if (geojson.type === 'GeometryCollection') { + r.geometries = r.geometries.map(function(gj) { return traverseGeoJson(gj, leafCallback, nodeCallback); }); + } else { + if (leafCallback) leafCallback(r); + } + + if (nodeCallback) nodeCallback(r); + + return r; +} + +function determineCrs(crs) { + if (typeof crs === 'string' || crs instanceof String) { + return Proj4js.defs(crs) ? new Proj4js.Proj(crs) : null; + } + return crs; +} + +const CoordinatesUtils = { getUnits: function(projection) { const proj = new Proj4js.Proj(projection); return proj.units || 'degrees'; @@ -27,6 +65,52 @@ var CoordinatesUtils = { } return null; }, + /** + * Reprojects a geojson from a crs into another + */ + reprojectGeoJson: function(geojson, fromParam = "EPSG:4326", toParam = "EPSG:4326") { + let from = fromParam; + let to = toParam; + if (typeof from === 'string') { + from = determineCrs(from); + } + if (typeof to === 'string') { + to = determineCrs(to); + } + let transform = proj4(from, to); + + return traverseGeoJson(geojson, (gj) => { + // No easy way to put correct CRS info into the GeoJSON, + // and definitely wrong to keep the old, so delete it. + if (gj.crs) { + delete gj.crs; + } + gj.coordinates = traverseCoords(gj.coordinates, (xy) => { + return transform.forward(xy); + }); + }, (gj) => { + if (gj.bbox) { + // A bbox can't easily be reprojected, just reprojecting + // the min/max coords definitely will not work since + // the transform is not linear (in the general case). + // Workaround is to just re-compute the bbox after the + // transform. + gj.bbox = (() => { + let min = [Number.MAX_VALUE, Number.MAX_VALUE]; + let max = [-Number.MAX_VALUE, -Number.MAX_VALUE]; + traverseGeoJson(gj, function(_gj) { + traverseCoords(_gj.coordinates, function(xy) { + min[0] = Math.min(min[0], xy[0]); + min[1] = Math.min(min[1], xy[1]); + max[0] = Math.max(max[0], xy[0]); + max[1] = Math.max(max[1], xy[1]); + }); + }); + return [min[0], min[1], max[0], max[1]]; + })(); + } + }); + }, normalizePoint: function(point) { return { x: point.x || 0.0, diff --git a/web/client/utils/PrintUtils.js b/web/client/utils/PrintUtils.js index 0397ded09b..da8419e05d 100644 --- a/web/client/utils/PrintUtils.js +++ b/web/client/utils/PrintUtils.js @@ -9,6 +9,7 @@ const CoordinatesUtils = require('./CoordinatesUtils'); const MapUtils = require('./MapUtils'); + const {isArray} = require('lodash'); const url = require('url'); @@ -17,6 +18,10 @@ const defaultScales = MapUtils.getGoogleMercatorScales(0, 21); const assign = require('object-assign'); +const getGeomType = function(layer) { + return (layer.features && layer.features[0]) ? layer.features[0].geometry.type : undefined; +}; + const PrintUtils = { normalizeUrl: (input) => { let result = isArray(input) ? input[0] : input; @@ -140,6 +145,23 @@ const PrintUtils = { ] }) }, + vector: { + map: (layer, spec) => ({ + type: 'Vector', + name: layer.name, + "opacity": layer.opacity || 1.0, + styleProperty: "ms_style", + styles: { + 1: PrintUtils.toOpenLayers2Style(layer, layer.style) + }, + geoJson: CoordinatesUtils.reprojectGeoJson({ + type: "FeatureCollection", + features: layer.features.map( f => ({...f, properties: {...f.properties, ms_style: 1}})) + }, + "EPSG:4326", + spec.projection) + }) + }, osm: { map: () => ({ "baseURL": "http://a.tile.openstreetmap.org/", @@ -220,6 +242,87 @@ const PrintUtils = { ] }) } + }, + /** + * Useful for print (Or generic Openlayers 2 conversion style) + */ + toOpenLayers2Style: function(layer, style) { + if (!style) { + return PrintUtils.getOlDefaultStyle(layer); + } + // commented the available options. + return { + "fillColor": style.fillColor, + "fillOpacity": style.fillOpacity, + // "rotation": "30", + // "graphicName": "circle", + // "graphicOpacity": 0.4, + "pointRadius": style.radius, + "strokeColor": style.color, + "strokeOpacity": style.opacity, + "strokeWidth": style.weight + // "strokeLinecap": "round", + // "strokeDashstyle": "dot", + // "fontColor": "#000000", + // "fontFamily": "sans-serif", + // "fontSize": "12px", + // "fontStyle": "normal", + // "fontWeight": "bold", + // "haloColor": "#123456", + // "haloOpacity": "0.7", + // "haloRadius": "3.0", + // "label": "${name}", + // "labelAlign": "cm", + // "labelRotation": "45", + // "labelXOffset": "-25.0", + // "labelYOffset": "-35.0" + }; + }, + /** + * Provides the default style for + * each vector type. + */ + getOlDefaultStyle(layer) { + switch (getGeomType(layer)) { + case 'Polygon': + case 'MultiPolygon': { + return { + "fillColor": "#0000FF", + "fillOpacity": 0.1, + "strokeColor": "#0000FF", + "strokeOpacity": 1, + "strokeWidth": 3 + }; + } + case 'MultiLineString': + case 'LineString': + return { + "strokeColor": "#0000FF", + "strokeOpacity": 1, + "strokeWidth": 3 + }; + case 'Point': + case 'MultiPoint': { + return { + "fillColor": "#FF0000", + "fillOpacity": 0, + "strokeColor": "#FF0000", + "pointRadius": 5, + "strokeOpacity": 1, + "strokeWidth": 1 + }; + } + default: { + return { + "fillColor": "#0000FF", + "fillOpacity": 0.1, + "strokeColor": "#0000FF", + "pointRadius": 5, + "strokeOpacity": 1, + "strokeWidth": 1 + }; + } + } } }; diff --git a/web/client/utils/__tests__/CoordinatesUtils-test.js b/web/client/utils/__tests__/CoordinatesUtils-test.js index 3547e33946..95c35d6036 100644 --- a/web/client/utils/__tests__/CoordinatesUtils-test.js +++ b/web/client/utils/__tests__/CoordinatesUtils-test.js @@ -72,4 +72,33 @@ describe('CoordinatesUtils', () => { expect(CoordinatesUtils.getCompatibleSRS('EPSG:3857', {'EPSG:900913': true, 'EPSG:3857': true})).toBe('EPSG:3857'); expect(CoordinatesUtils.getCompatibleSRS('EPSG:3857', {'EPSG:3857': true})).toBe('EPSG:3857'); }); + it('test reprojectGeoJson', () => { + const testPoint = { + type: "FeatureCollection", + features: [ + { + type: "Feature", + geometry: { + type: "Point", + coordinates: [ + -112.50042920000001, + 42.22829164089942 + ] + }, + properties: { + "serial_num": "12C324776" + }, + id: 0 + } + ] + }; + const reprojectedTestPoint = CoordinatesUtils.reprojectGeoJson(testPoint, "EPSG:4326", "EPSG:900913"); + expect(reprojectedTestPoint).toExist(); + expect(reprojectedTestPoint.features).toExist(); + expect(reprojectedTestPoint.features[0]).toExist(); + expect(reprojectedTestPoint.features[0].type).toBe("Feature"); + expect(reprojectedTestPoint.features[0].geometry.type).toBe("Point"); + expect(reprojectedTestPoint.features[0].geometry.coordinates[0]).toBe(-12523490.492568726); + expect(reprojectedTestPoint.features[0].geometry.coordinates[1]).toBe(5195238.005360028); + }); }); diff --git a/web/client/utils/__tests__/PrintUtils-test.js b/web/client/utils/__tests__/PrintUtils-test.js index 2cf2a9cde3..46f1d9082d 100644 --- a/web/client/utils/__tests__/PrintUtils-test.js +++ b/web/client/utils/__tests__/PrintUtils-test.js @@ -7,6 +7,7 @@ */ const expect = require('expect'); const PrintUtils = require('../PrintUtils'); +const {isEqual} = require('lodash'); const layer = { url: "http://mygeoserver", @@ -15,7 +16,74 @@ const layer = { params: {myparam: "myvalue"} }; - +const vectorLayer = { + "type": "vector", + "visibility": true, + "group": "Local shape", + "id": "web2014all_mv__14", + "name": "web2014all_mv", + "hideLoading": true, + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + -112.50042920000001, + 42.22829164089942 + ] + }, + "properties": { + "serial_num": "12C324776" + }, + "id": 0 + } + ], + "style": { + "weight": 3, + "radius": 10, + "opacity": 1, + "fillOpacity": 0.1, + "color": "rgb(0, 0, 255)", + "fillColor": "rgb(0, 0, 255)" + } +}; +const mapFishVectorLayer = { + "type": "Vector", + "name": "web2014all_mv", + "opacity": 1, + "styleProperty": "ms_style", + "styles": { + "1": { + "fillColor": "rgb(0, 0, 255)", + "fillOpacity": 0.1, + "pointRadius": 10, + "strokeColor": "rgb(0, 0, 255)", + "strokeOpacity": 1, + "strokeWidth": 3 + } + }, + "geoJson": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + -12523490.492568726, + 5195238.005360028 + ] + }, + "properties": { + "serial_num": "12C324776", + "ms_style": 1 + }, + "id": 0 + } + ] + } +}; describe('PrintUtils', () => { it('custom params are applied to wms layers', () => { @@ -26,4 +94,26 @@ describe('PrintUtils', () => { expect(specs[0].customParams.myparam).toExist(); expect(specs[0].customParams.myparam).toBe("myvalue"); }); + it('vector layer generation for print', () => { + const specs = PrintUtils.getMapfishLayersSpecification([vectorLayer], {projection: "EPSG:900913"}, 'map'); + expect(specs).toExist(); + expect(specs.length).toBe(1); + expect(isEqual(specs[0], mapFishVectorLayer)).toBe(true); + }); + it('vector layer default point style', () => { + const style = PrintUtils.getOlDefaultStyle({features: [{geometry: {type: "Point"}}]}); + expect(style).toExist(); + expect(style.pointRadius).toBe(5); + }); + it('vector layer default polygon style', () => { + const style = PrintUtils.getOlDefaultStyle({features: [{geometry: {type: "Polygon"}}]}); + expect(style).toExist(); + expect(style.strokeWidth).toBe(3); + + }); + it('vector layer default line style', () => { + const style = PrintUtils.getOlDefaultStyle({features: [{geometry: {type: "LineString"}}]}); + expect(style).toExist(); + expect(style.strokeWidth).toBe(3); + }); }); diff --git a/web/client/utils/openlayers/StyleUtils.js b/web/client/utils/openlayers/StyleUtils.js index 5d18027f74..2ddd6d5cee 100644 --- a/web/client/utils/openlayers/StyleUtils.js +++ b/web/client/utils/openlayers/StyleUtils.js @@ -20,6 +20,14 @@ const toVectorStyle = function(layer, style) { if (style.marker && (geomT === 'Point' || geomT === 'MultiPoint')) { newLayer.styleName = "marker"; }else { + newLayer.style = { + weight: style.width, + radius: style.radius, + opacity: style.color.a, + fillOpacity: style.fill.a, + color: getColor(style.color), + fillColor: getColor(style.fill) + }; let stroke = new ol.style.Stroke({ color: getColor(style.color), width: style.width From a307f77afbc339cdab9943c6ff4f0d07e6cff1e2 Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Thu, 1 Dec 2016 18:54:20 +0100 Subject: [PATCH 2/6] Add default marker and preview for vector print - Now you can print also markers - Now the preview shows the vector layer **note**: a little bug have to be solved. The preview map do not show features on first rendering. This can be avoided loading them in the VectorLayer on startup, but this will cause duplicated vectors data. --- web/client/components/print/MapPreview.jsx | 24 ++++++++++++++++++- web/client/utils/PrintUtils.js | 9 ++++++- web/client/utils/__tests__/PrintUtils-test.js | 10 +++++--- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/web/client/components/print/MapPreview.jsx b/web/client/components/print/MapPreview.jsx index 13e95d6dc4..10c639b555 100644 --- a/web/client/components/print/MapPreview.jsx +++ b/web/client/components/print/MapPreview.jsx @@ -14,6 +14,7 @@ const {Button, Glyphicon} = require('react-bootstrap'); let PMap; let Layer; +let Feature; const MapPreview = React.createClass({ propTypes: { @@ -54,6 +55,7 @@ const MapPreview = React.createClass({ PMap = require('../map/' + this.props.mapType + '/Map'); Layer = require('../map/' + this.props.mapType + '/Layer'); require('../map/' + this.props.mapType + '/plugins/index'); + Feature = require('../map/' + this.props.mapType + '/index').Feature; }, getRatio() { if (this.props.width && this.props.layoutSize && this.props.resolutions) { @@ -77,6 +79,23 @@ const MapPreview = React.createClass({ }) }); }, + renderLayerContent(layer) { + if (layer.features && layer.type === "vector") { + return layer.features.map( (feature) => { + return ( + + ); + }); + } + return null; + }, render() { const style = assign({}, this.props.style, { width: this.props.width + "px", @@ -103,7 +122,10 @@ const MapPreview = React.createClass({ > {this.props.layers.map((layer, index) => + options={assign({}, this.adjustResolution(layer), {srs: projection})}> + {this.renderLayerContent(layer)} + + )} {this.props.enableScalebox ? { expect(specs[0].customParams.myparam).toBe("myvalue"); }); it('vector layer generation for print', () => { - const specs = PrintUtils.getMapfishLayersSpecification([vectorLayer], {projection: "EPSG:900913"}, 'map'); + const specs = PrintUtils.getMapfishLayersSpecification([vectorLayer], {projection: "EPSG:3857"}, 'map'); expect(specs).toExist(); expect(specs.length).toBe(1); - expect(isEqual(specs[0], mapFishVectorLayer)).toBe(true); + expect(specs[0].geoJson.features[0].geometry.coordinates[0], mapFishVectorLayer).toBe(mapFishVectorLayer.geoJson.features[0].geometry.coordinates[0]); }); it('vector layer default point style', () => { const style = PrintUtils.getOlDefaultStyle({features: [{geometry: {type: "Point"}}]}); expect(style).toExist(); expect(style.pointRadius).toBe(5); }); + it('vector layer default marker style', () => { + const style = PrintUtils.getOlDefaultStyle({styleName: "marker", features: [{geometry: {type: "Point"}}]}); + expect(style).toExist(); + expect(style.externalGraphic).toExist(); + }); it('vector layer default polygon style', () => { const style = PrintUtils.getOlDefaultStyle({features: [{geometry: {type: "Polygon"}}]}); expect(style).toExist(); From ad6ce08d97b4b19449bc5dc5df971a2dc788cd01 Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Fri, 2 Dec 2016 11:57:24 +0100 Subject: [PATCH 3/6] fixed preview issues for same named layer --- web/client/components/print/MapPreview.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/client/components/print/MapPreview.jsx b/web/client/components/print/MapPreview.jsx index 10c639b555..abf505dddf 100644 --- a/web/client/components/print/MapPreview.jsx +++ b/web/client/components/print/MapPreview.jsx @@ -89,7 +89,6 @@ const MapPreview = React.createClass({ geometry={feature.geometry} msId={feature.id} featuresCrs={ layer.featuresCrs || 'EPSG:4326' } - // FEATURE STYLE OVERWRITE LAYER STYLE style={ feature.style || layer.style || null }/> ); }); @@ -121,7 +120,7 @@ const MapPreview = React.createClass({ mapOptions={mapOptions} > {this.props.layers.map((layer, index) => - {this.renderLayerContent(layer)} From 346275d0506de625961a3b29c0153e0d7699919a Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Fri, 2 Dec 2016 12:23:15 +0100 Subject: [PATCH 4/6] ForceUpdate when layer's created. Optimized leaflet layer rendering --- web/client/components/map/leaflet/Layer.jsx | 22 +++++++++++++++++++ .../components/map/openlayers/Layer.jsx | 1 + 2 files changed, 23 insertions(+) diff --git a/web/client/components/map/leaflet/Layer.jsx b/web/client/components/map/leaflet/Layer.jsx index 878406d19c..38c561a39f 100644 --- a/web/client/components/map/leaflet/Layer.jsx +++ b/web/client/components/map/leaflet/Layer.jsx @@ -8,6 +8,7 @@ var React = require('react'); var Layers = require('../../../utils/leaflet/Layers'); var assign = require('object-assign'); +var {isEqual} = require('lodash'); const LeafletLayer = React.createClass({ propTypes: { @@ -45,6 +46,26 @@ const LeafletLayer = React.createClass({ } this.updateLayer(newProps, this.props); }, + shouldComponentUpdate(newProps) { + // the reduce returns true when a prop is changed + // optimizing when options are equal ignorning loading key + return !(["map", "type", "srs", "position", "zoomOffset", "onInvalid", "onClick", "options"].reduce( (prev, p) => { + switch (p) { + case "map": + case "type": + case "srs": + case "position": + case "zoomOffset": + case "onInvalid": + case "onClick": + return prev && this.props[p] === newProps[p]; + case "options": + return prev && (this.props[p] === newProps[p] || isEqual({...this.props[p], loading: false}, {...newProps[p], loading: false})); + default: + return prev; + } + }, true)); + }, componentWillUnmount() { if (this.layer && this.props.map) { this.removeLayer(); @@ -105,6 +126,7 @@ const LeafletLayer = React.createClass({ this.layer.layerName = options.name; this.layer.layerId = options.id; } + this.forceUpdate(); } }, updateLayer(newProps, oldProps) { diff --git a/web/client/components/map/openlayers/Layer.jsx b/web/client/components/map/openlayers/Layer.jsx index 63a1d55ed5..e4e74f5e5b 100644 --- a/web/client/components/map/openlayers/Layer.jsx +++ b/web/client/components/map/openlayers/Layer.jsx @@ -104,6 +104,7 @@ const OpenlayersLayer = React.createClass({ if (this.layer && !this.layer.detached) { this.addLayer(options); } + this.forceUpdate(); } }, updateLayer(newProps, oldProps) { From fd2b4660c240d2315a9301ba10c4dde98c7a771d Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Fri, 2 Dec 2016 12:58:48 +0100 Subject: [PATCH 5/6] rounded reprojection coordinates tests --- web/client/utils/__tests__/CoordinatesUtils-test.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/client/utils/__tests__/CoordinatesUtils-test.js b/web/client/utils/__tests__/CoordinatesUtils-test.js index 95c35d6036..c007b8e0fe 100644 --- a/web/client/utils/__tests__/CoordinatesUtils-test.js +++ b/web/client/utils/__tests__/CoordinatesUtils-test.js @@ -98,7 +98,8 @@ describe('CoordinatesUtils', () => { expect(reprojectedTestPoint.features[0]).toExist(); expect(reprojectedTestPoint.features[0].type).toBe("Feature"); expect(reprojectedTestPoint.features[0].geometry.type).toBe("Point"); - expect(reprojectedTestPoint.features[0].geometry.coordinates[0]).toBe(-12523490.492568726); - expect(reprojectedTestPoint.features[0].geometry.coordinates[1]).toBe(5195238.005360028); + // approximate values should be the same + expect(reprojectedTestPoint.features[0].geometry.coordinates[0].toFixed(4)).toBe((-12523490.492568726).toFixed(4)); + expect(reprojectedTestPoint.features[0].geometry.coordinates[1].toFixed(4)).toBe((5195238.005360028).toFixed(4)); }); }); From 15cb53cef405bd01033d330c8a1fd5574425ec36 Mon Sep 17 00:00:00 2001 From: Lorenzo Natali Date: Fri, 2 Dec 2016 15:39:58 +0100 Subject: [PATCH 6/6] add tests for feature and vector layers --- web/client/components/map/leaflet/Feature.jsx | 2 +- .../map/leaflet/__tests__/Layer-test.jsx | 115 ++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/web/client/components/map/leaflet/Feature.jsx b/web/client/components/map/leaflet/Feature.jsx index 17f2d21e23..678cd9efc1 100644 --- a/web/client/components/map/leaflet/Feature.jsx +++ b/web/client/components/map/leaflet/Feature.jsx @@ -107,7 +107,7 @@ var geometryToLayer = function(geojson, options) { return new L.FeatureGroup(layers); case 'GeometryCollection': for (i = 0, len = geometry.geometries.length; i < len; i++) { - layer = this.geometryToLayer({ + layer = geometryToLayer({ geometry: geometry.geometries[i], type: 'Feature', properties: geojson.properties diff --git a/web/client/components/map/leaflet/__tests__/Layer-test.jsx b/web/client/components/map/leaflet/__tests__/Layer-test.jsx index 620ba1d2d2..25e75909f2 100644 --- a/web/client/components/map/leaflet/__tests__/Layer-test.jsx +++ b/web/client/components/map/leaflet/__tests__/Layer-test.jsx @@ -9,6 +9,7 @@ var React = require('react/addons'); var ReactDOM = require('react-dom'); var L = require('leaflet'); var LeafLetLayer = require('../Layer.jsx'); +var Feature = require('../Feature.jsx'); var expect = require('expect'); require('../../../../utils/leaflet/Layers'); @@ -18,6 +19,7 @@ require('../plugins/WMSLayer'); require('../plugins/GoogleLayer'); require('../plugins/BingLayer'); require('../plugins/MapQuest'); +require('../plugins/VectorLayer'); describe('Leaflet layer', () => { let map; @@ -177,6 +179,119 @@ describe('Leaflet layer', () => { expect(urls.length).toBe(1); }); + it('creates a vector layer for leaflet map', () => { + var options = { + "type": "wms", + "visibility": true, + "name": "vector_sample", + "group": "sample", + "features": [ + { "type": "Feature", + "geometry": {"type": "Point", "coordinates": [102.0, 0.5]}, + "properties": {"prop0": "value0"} + }, + { "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0] + ] + }, + "properties": { + "prop0": "value0", + "prop1": 0.0 + } + }, + { "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], + [100.0, 1.0], [100.0, 0.0] ] + ] + }, + "properties": { + "prop0": "value0", + "prop1": {"this": "that"} + } + }, + { "type": "Feature", + "geometry": { "type": "MultiPoint", + "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] + }, + "properties": { + "prop0": "value0", + "prop1": {"this": "that"} + } + }, + { "type": "Feature", + "geometry": { "type": "MultiLineString", + "coordinates": [ + [ [100.0, 0.0], [101.0, 1.0] ], + [ [102.0, 2.0], [103.0, 3.0] ] + ] + }, + "properties": { + "prop0": "value0", + "prop1": {"this": "that"} + } + }, + { "type": "Feature", + "geometry": { "type": "MultiPolygon", + "coordinates": [ + [[[102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0]]], + [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]], + [[100.2, 0.2], [100.8, 0.2], [100.8, 0.8], [100.2, 0.8], [100.2, 0.2]]] + ] + }, + "properties": { + "prop0": "value0", + "prop1": {"this": "that"} + } + }, + { "type": "Feature", + "geometry": { "type": "GeometryCollection", + "geometries": [ + { "type": "Point", + "coordinates": [100.0, 0.0] + }, + { "type": "LineString", + "coordinates": [ [101.0, 0.0], [102.0, 1.0] ] + } + ] + }, + "properties": { + "prop0": "value0", + "prop1": {"this": "that"} + } + } + ] + }; + // create layers + var layer = ReactDOM.render( + ( + {options.features.map((feature) => ())}), document.getElementById("container")); + expect(layer).toExist(); + let l2 = ReactDOM.render( + ( + {options.features.map((feature) => ())}), document.getElementById("container")); + expect(l2).toExist(); + }); + it('creates a wms layer for leaflet map with custom tileSize', () => { var options = { "type": "wms",