From af7245b90277337e44427c0df7a307102fc481c1 Mon Sep 17 00:00:00 2001 From: ncastejon <castejon.nicolas@gmail.com> Date: Fri, 8 Jun 2018 09:06:37 +0200 Subject: [PATCH] Add Openlayers map, WMS & WFS layers, popup and interaction. All WIP code. To refactor --- webapp/.angular-cli.json | 7 +- webapp/package-lock.json | 120 +++-- webapp/package.json | 11 +- .../dataset-map/dataset-map.component.html | 7 +- .../dataset-map/dataset-map.component.scss | 45 +- .../dataset-map/dataset-map.component.ts | 314 +++++++---- webapp/src/assets/leaflet.wms.js | 495 ------------------ webapp/src/tsconfig.app.json | 6 +- 8 files changed, 351 insertions(+), 654 deletions(-) delete mode 100644 webapp/src/assets/leaflet.wms.js diff --git a/webapp/.angular-cli.json b/webapp/.angular-cli.json index a0ecf192..9755499b 100644 --- a/webapp/.angular-cli.json +++ b/webapp/.angular-cli.json @@ -20,13 +20,10 @@ "prefix": "app", "styles": [ "styles.scss", - "../node_modules/leaflet/dist/leaflet.css", - "../node_modules/leaflet.markercluster/dist/MarkerCluster.css", - "../node_modules/leaflet.markercluster/dist/MarkerCluster.Default.css" + "../node_modules/ol/ol.css" ], "scripts": [ - "../node_modules/leaflet.markercluster/dist/leaflet.markercluster.js", - "../node_modules/proj4leaflet/lib/proj4-compressed.js" + "../node_modules/ol-layerswitcher/dist/ol-layerswitcher.js" ], "environmentSource": "environments/environment.ts", "environments": { diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 00dd7ed8..38cf3225 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -283,11 +283,6 @@ "semver-intersect": "1.3.1" } }, - "@types/geojson": { - "version": "7946.0.3", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.3.tgz", - "integrity": "sha512-BYHiG1vQJ7T93uswzuXZ0OBPWqj5tsAPtaMDQADV8sn2InllXarwg9llr6uaW22q1QCwBZ81gVajOpYWzjesug==" - }, "@types/jasmine": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.6.tgz", @@ -303,43 +298,30 @@ "@types/jasmine": "2.8.6" } }, - "@types/leaflet": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.2.7.tgz", - "integrity": "sha512-y9KEOzVB1uIPVPOc8FBqQjBvFykb/WhNfHDN6eTL8FlUy6F7FaqGMWHj5OhDSBHMWoqc70eR3FGiXdzbg1rqtg==", - "requires": { - "@types/geojson": "7946.0.3" - } - }, - "@types/leaflet.markercluster": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/leaflet.markercluster/-/leaflet.markercluster-1.0.3.tgz", - "integrity": "sha512-rz4xQcsD3Ha9TcX4nMba9wpNe7HPQ03Hvo8Osi3SLpfaDCydHMoTquOG1IsjQ2aFm/LIHz4Uo4hYoeLv7q082w==", - "requires": { - "@types/leaflet": "1.2.7" - } - }, "@types/node": { "version": "6.0.102", "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.102.tgz", "integrity": "sha512-EhNufyBoC1Kqaj+XMHGzi6mPUC8wVABOMTPE5XaSJc49LIVvXsyrV1HYMAPTUViT7E4wLUB38OdDmb+HshjGmA==", "dev": true }, + "@types/ol": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@types/ol/-/ol-4.6.2.tgz", + "integrity": "sha512-nXmOOkNjrHpTLHIN6H1Y7q9u6s/uSSnUi1va9n37FFjtf8O6IYSAll/uzKv+jqNOoN9SFx/RYSyhRK8Hs460BA==", + "requires": { + "@types/openlayers": "4.6.9" + } + }, + "@types/openlayers": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/@types/openlayers/-/openlayers-4.6.9.tgz", + "integrity": "sha512-Q668pU0J+8QwrCTpk9Yz1jTHqjZv0+NvfZ1UuyucsaW8/BXnTzdsOfkInJRX22XGwFmE1AXcDkB9lydkwmCxCQ==" + }, "@types/proj4": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/@types/proj4/-/proj4-2.3.4.tgz", "integrity": "sha1-84i2AgnEy3XsIaF6S0rFWnGlhVg=" }, - "@types/proj4leaflet": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/proj4leaflet/-/proj4leaflet-1.0.5.tgz", - "integrity": "sha512-xqw5V5wk0iSjpDSN2X1tuEo6qL4QhfffaTpnHN6ISz3vnT4Z4m4aKdH1vQHlpJ6aJ0TBvcRmI8X2/Lv7t/0qOw==", - "requires": { - "@types/geojson": "7946.0.3", - "@types/leaflet": "1.2.7", - "@types/proj4": "2.3.4" - } - }, "@types/q": { "version": "0.0.32", "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", @@ -5388,8 +5370,7 @@ "ieee754": { "version": "1.1.10", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.10.tgz", - "integrity": "sha512-byWFX8OyW/qeVxcY21r6Ncxl0ZYHgnf0cPup2h34eHXrCJbOp7IuqnJ4Q0omfyWl6Z++BTI6bByf31pZt7iRLg==", - "dev": true + "integrity": "sha512-byWFX8OyW/qeVxcY21r6Ncxl0ZYHgnf0cPup2h34eHXrCJbOp7IuqnJ4Q0omfyWl6Z++BTI6bByf31pZt7iRLg==" }, "iferr": { "version": "0.1.5", @@ -6383,16 +6364,6 @@ "invert-kv": "1.0.0" } }, - "leaflet": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.3.1.tgz", - "integrity": "sha512-adQOIzh+bfdridLM1xIgJ9VnJbAUY3wqs/ueF+ITla+PLQ1z47USdBKUf+iD9FuUA8RtlT6j6hZBfZoA6mW+XQ==" - }, - "leaflet.markercluster": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.3.0.tgz", - "integrity": "sha512-bHBaI8kTaLrZxI/ZP7pnNaS8WCKKz98BU1//B5OH9b/kTk6IriduvDtMFGlGBYYr/ymuXCB9lO7HsGBr5dSw5w==" - }, "less": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/less/-/less-2.7.3.tgz", @@ -7815,6 +7786,21 @@ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true }, + "ol": { + "version": "4.6.5", + "resolved": "https://registry.npmjs.org/ol/-/ol-4.6.5.tgz", + "integrity": "sha512-FtXsY3WY07c5vEP53rUkkw3jN7orcmg07lbmTfeS2hGyIZ0Noy1xC5urP+n6XTggnQZv0NlOkAt9lxNEmAQWQw==", + "requires": { + "pbf": "3.1.0", + "pixelworks": "1.1.0", + "rbush": "2.0.1" + } + }, + "ol-layerswitcher": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ol-layerswitcher/-/ol-layerswitcher-2.0.0.tgz", + "integrity": "sha512-Gf1o+iZMR15aOTUUz5HKOgNtNRKR+VXm6JYzfe6VvF7QWVa5WTcsU2qdtcRHBOwOsMyxggZAFzUxre6g9Dg35Q==" + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -8217,6 +8203,15 @@ "pify": "3.0.0" } }, + "pbf": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.1.0.tgz", + "integrity": "sha512-/hYJmIsTmh7fMkHAWWXJ5b8IKLWdjdlAFb3IHkRBn1XUhIYBChVGfVwmHEAV3UfXTxsP/AKfYTXTS/dCPxJd5w==", + "requires": { + "ieee754": "1.1.10", + "resolve-protobuf-schema": "2.0.0" + } + }, "pbkdf2": { "version": "3.0.14", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", @@ -8257,6 +8252,11 @@ "pinkie": "2.0.4" } }, + "pixelworks": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pixelworks/-/pixelworks-1.1.0.tgz", + "integrity": "sha1-Hwla1I3Ki/ihyCWOAJIDGkTyLKU=" + }, "pkg-dir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", @@ -8454,14 +8454,6 @@ "wkt-parser": "1.2.1" } }, - "proj4leaflet": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/proj4leaflet/-/proj4leaflet-1.0.2.tgz", - "integrity": "sha512-6GdDeUlhX/tHUiMEj80xQhlPjwrXcdfD0D5OBymY8WvxfbmZcdhNqQk7n7nFf53ue6QdP9ls9ZPjsAxnbZDTsw==", - "requires": { - "proj4": "2.4.4" - } - }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -8478,6 +8470,11 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, + "protocol-buffers-schema": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-2.2.0.tgz", + "integrity": "sha1-0pxs1z+2VZePtpiWkRgNuEQRn2E=" + }, "protractor": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.1.2.tgz", @@ -8712,6 +8709,11 @@ "integrity": "sha1-DPf4T5Rj/wrlHExLFC2VvjdyTZw=", "dev": true }, + "quickselect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-1.1.1.tgz", + "integrity": "sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ==" + }, "randomatic": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", @@ -8796,6 +8798,14 @@ "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=", "dev": true }, + "rbush": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-2.0.1.tgz", + "integrity": "sha1-TPrKKMMGS8DudUMaG3mZDode76k=", + "requires": { + "quickselect": "1.1.1" + } + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -9171,6 +9181,14 @@ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", "dev": true }, + "resolve-protobuf-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.0.0.tgz", + "integrity": "sha1-5nsGKmfwLRG9aIbnDv2niEB+D7Q=", + "requires": { + "protocol-buffers-schema": "2.2.0" + } + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", diff --git a/webapp/package.json b/webapp/package.json index 16e52070..ad1260a3 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -30,15 +30,14 @@ "@angular/platform-browser": "^5.2.0", "@angular/platform-browser-dynamic": "^5.2.0", "@angular/router": "^5.2.0", - "@types/leaflet": "^1.2.7", - "@types/leaflet.markercluster": "^1.0.3", - "@types/proj4leaflet": "^1.0.5", + "@types/ol": "^4.6.2", + "@types/proj4": "^2.3.4", "bulma": "^0.7.1", "core-js": "^2.4.1", "font-awesome": "^4.7.0", - "leaflet": "^1.3.1", - "leaflet.markercluster": "^1.3.0", - "proj4leaflet": "^1.0.2", + "ol": "^4.6.5", + "ol-layerswitcher": "^2.0.0", + "proj4": "^2.4.4", "rxjs": "^5.5.6", "zone.js": "^0.8.19" }, diff --git a/webapp/src/app/geosource/components/dataset-detail/dataset-map/dataset-map.component.html b/webapp/src/app/geosource/components/dataset-detail/dataset-map/dataset-map.component.html index c0c8e3ce..6b38e6a0 100644 --- a/webapp/src/app/geosource/components/dataset-detail/dataset-map/dataset-map.component.html +++ b/webapp/src/app/geosource/components/dataset-detail/dataset-map/dataset-map.component.html @@ -1,3 +1,6 @@ -<div id="frugalmap"> - +<div id="map"> +</div> +<div id="popup" class="ol-popup"> + <a href="#" id="popup-closer" class="ol-popup-closer"></a> + <div id="popup-content"></div> </div> \ No newline at end of file diff --git a/webapp/src/app/geosource/components/dataset-detail/dataset-map/dataset-map.component.scss b/webapp/src/app/geosource/components/dataset-detail/dataset-map/dataset-map.component.scss index 4fb3c0b7..a8b33f5f 100644 --- a/webapp/src/app/geosource/components/dataset-detail/dataset-map/dataset-map.component.scss +++ b/webapp/src/app/geosource/components/dataset-detail/dataset-map/dataset-map.component.scss @@ -1,3 +1,46 @@ -#frugalmap { +#map { height: 600px; } + +.ol-popup { + position: absolute; + background-color: white; + -webkit-filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); + filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); + padding: 15px; + border-radius: 10px; + border: 1px solid #cccccc; + bottom: 12px; + left: -50px; + min-width: 280px; +} +.ol-popup:after, .ol-popup:before { + top: 100%; + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; +} +.ol-popup:after { + border-top-color: white; + border-width: 10px; + left: 48px; + margin-left: -10px; +} +.ol-popup:before { + border-top-color: #cccccc; + border-width: 11px; + left: 48px; + margin-left: -11px; +} +.ol-popup-closer { + text-decoration: none; + position: absolute; + top: 2px; + right: 8px; +} +.ol-popup-closer:after { + content: "✖"; +} \ No newline at end of file diff --git a/webapp/src/app/geosource/components/dataset-detail/dataset-map/dataset-map.component.ts b/webapp/src/app/geosource/components/dataset-detail/dataset-map/dataset-map.component.ts index bfa9bc44..3e866bdc 100644 --- a/webapp/src/app/geosource/components/dataset-detail/dataset-map/dataset-map.component.ts +++ b/webapp/src/app/geosource/components/dataset-detail/dataset-map/dataset-map.component.ts @@ -1,12 +1,34 @@ import { Component, OnInit, Input } from '@angular/core'; -import * as L from 'leaflet'; -import 'leaflet.markercluster'; -import 'proj4leaflet'; import { ActivatedRoute, ParamMap } from '@angular/router'; import { DatasetService } from '../../../services'; import { Metadata, Data } from '../../../models'; import { MapService } from '../../../services/map.service'; -import * as wms from '../../../../../assets/leaflet.wms.js'; +import proj4 from 'proj4'; +import olMap from 'ol/map'; +import olView from 'ol/view'; +import olOverlay from 'ol/overlay'; +import olSourceOSM from 'ol/source/osm'; +import olTileWMS from 'ol/source/tilewms'; +import olSourceXYZ from 'ol/source/xyz'; +import olPoint from 'ol/geom/point'; +import olLayerTile from 'ol/layer/tile'; +import olLayerVector from 'ol/layer/vector'; +import olTileImage from 'ol/source/tileimage'; +import olFormatGeoJson from 'ol/format/geojson'; +import olFormatWFS from 'ol/format/wfs'; +import olSourceVector from 'ol/source/vector'; +import olSourceCluster from 'ol/source/cluster'; +import olTileGrid from 'ol/tilegrid/tilegrid'; +import olProj from 'ol/proj'; +import olLoadingStrategy from 'ol/loadingstrategy'; +import olStyle from 'ol/style/style'; +import olStroke from 'ol/style/stroke'; +import olFill from 'ol/style/fill'; +import olCircle from 'ol/style/circle'; +import olSelect from 'ol/interaction/select'; +import olModify from 'ol/interaction/modify'; +import olInteraction from 'ol/interaction'; +import olDragRotateAndZoom from 'ol/interaction/dragrotateandzoom'; @Component({ selector: 'app-dataset-map', @@ -32,110 +54,220 @@ export class DatasetMapComponent implements OnInit { } constructMap(metadata: Metadata) { // Déclaration de la carte avec les coordonnées du centre et le niveau de zoom. - const attribution = 'Data Grand Lyon'; - const satellite = L.tileLayer('https://openstreetmap.data.grandlyon.com/3857/tms/1.0.0/ortho2015@GoogleMapsCompatible/{z}/{x}/{-y}.png', { id: 'MapID', attribution: attribution }); - const grayscale = L.tileLayer('https://openstreetmap.data.grandlyon.com/3857/tms/1.0.0/osm_grandlyon_nb@GoogleMapsCompatible/{z}/{x}/{-y}.png', { id: 'MapID', attribution: attribution }); - const plan = L.tileLayer('https://openstreetmap.data.grandlyon.com/3857/tms/1.0.0/osm_grandlyon@GoogleMapsCompatible/{z}/{x}/{-y}.png', { id: 'MapID', attribution: attribution }); + // const attribution = 'Data Grand Lyon'; + // const satellite = L.tileLayer('https://openstreetmap.data.grandlyon.com/3857/tms/1.0.0/ortho2015@GoogleMapsCompatible/{z}/{x}/{-y}.png', { id: 'MapID', attribution: attribution }); + // const grayscale = L.tileLayer('https://openstreetmap.data.grandlyon.com/3857/tms/1.0.0/osm_grandlyon_nb@GoogleMapsCompatible/{z}/{x}/{-y}.png', { id: 'MapID', attribution: attribution }); + // const plan = L.tileLayer('https://openstreetmap.data.grandlyon.com/3857/tms/1.0.0/osm_grandlyon@GoogleMapsCompatible/{z}/{x}/{-y}.png', { id: 'MapID', attribution: attribution }); - const dataGrandLyonMap = L.map('frugalmap', { minZoom: 9, maxZoom: 18, layers: [plan] }) - .setView([45.75, 4.85], 12); + // const dataGrandLyonMap = L.map('frugalmap', { minZoom: 9, maxZoom: 18, layers: [plan] }) + // .setView([45.75, 4.85], 12); - const baseMaps = { - 'Plan': plan, - 'Niveau de gris': grayscale, - 'Satellite': satellite + // const baseMaps = { + // 'Plan': plan, + // 'Niveau de gris': grayscale, + // 'Satellite': satellite + // }; + // L.control.layers(baseMaps).addTo(dataGrandLyonMap); + // // Set icon marker + // const myIcon = L.icon({ + // iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.2.0/images/marker-icon.png' + // }); + + const container = document.getElementById('popup'); + const content = document.getElementById('popup-content'); + const closer = document.getElementById('popup-closer'); + const overlay = new olOverlay({ + element: container, + autoPan: true, + autoPanAnimation: { + duration: 250 + } + }); + /** + * Add a click handler to hide the popup. + * @return {boolean} Don't follow the href. + */ + closer.onclick = function () { + overlay.setPosition(undefined); + closer.blur(); + return false; + }; + + const defaultStyle = { + 'Point': new olStyle({ + image: new olCircle({ + fill: new olFill({ + color: 'lightgreen' + }), + radius: 8, + stroke: new olStroke({ + color: 'red', + width: 1 + }) + }) + }), + 'LineString': new olStyle({ + stroke: new olStroke({ + color: '#f00', + width: 3 + }) + }), + 'Polygon': new olStyle({ + fill: new olFill({ + color: 'rgba(170, 214, 204, 0.2)', + }), + stroke: new olStroke({ + color: 'blue', + width: 1 + }) + }), + 'MultiPoint': new olStyle({ + image: new olCircle({ + fill: new olFill({ + color: 'rgba(255,0,255,0.5)' + }), + radius: 5, + stroke: new olStroke({ + color: '#f0f', + width: 1 + }) + }) + }), + 'MultiLineString': new olStyle({ + stroke: new olStroke({ + color: '#0f0', + width: 3 + }) + }), + 'MultiPolygon': new olStyle({ + fill: new olFill({ + color: 'rgba(0,0,255,0.5)' + }), + stroke: new olStroke({ + color: '#00f', + width: 1 + }) + }) + }; + + const styleFunction = function (feature, resolution) { + const featureStyleFunction = feature.getStyleFunction(); + if (featureStyleFunction) { + return featureStyleFunction.call(feature, resolution); + } else { + return defaultStyle[feature.getGeometry().getType()]; + } }; - L.control.layers(baseMaps).addTo(dataGrandLyonMap); - // Set icon marker - const myIcon = L.icon({ - iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.2.0/images/marker-icon.png' + + + const map = new olMap({ + target: 'map', + overlays: [overlay], + interactions: olInteraction.defaults().extend([ + new olDragRotateAndZoom() + ]), + }); + + const planLayer = new olLayerTile({ + source: new olSourceXYZ({ + attributions: ['Grand Lyon'], + url: 'https://openstreetmap.data.grandlyon.com/3857/tms/1.0.0/osm_grandlyon@GoogleMapsCompatible/{z}/{x}/{-y}.png', + }) }); + olProj.setProj4(proj4); + proj4.defs('EPSG:4171', '+proj=longlat +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +no_defs '); + map.setView(new olView({ + maxZoom: 18, + center: olProj.fromLonLat([4.85, 45.75]), + zoom: 12 + })); + + map.addLayer(planLayer); - const group = []; - const cluster = L.markerClusterGroup({ - maxClusterRadius: 120 + /* Lyon image */ + const vectorSource = new olSourceVector({ + format: new olFormatGeoJson(), + url: function (extent) { + return 'https://download.data.grandlyon.com/wfs/grandlyon?SERVICE=WFS&VERSION=2.0.0&outputformat=GEOJSON&maxfeatures=30&request=GetFeature&typename=' + metadata.uri[0].name + '&srsname=EPSG:3857&' + + ',EPSG:3857'; + }, + strategy: olLoadingStrategy.bbox, }); - const hasWFS = metadata.uri.filter(function (e) { return e.protocol === 'OGC:WFS'; }).length > 0; - metadata.uri.forEach(element => { - if (hasWFS && element.protocol === 'OGC:WFS') { - const options = { - 'name': element.name - }; - this.mapService.getWFS(options).subscribe(results => { - console.log(results); - if (results.features[0].geometry.type === 'Point') { - L.geoJSON(results, { - pointToLayer: function (feature, latlng) { - return L.marker(latlng, { icon: myIcon }); // The basic style - } - }).addTo(dataGrandLyonMap); - } else if (results.features[0].geometry.type === 'Polygon') { - L.geoJSON(results, { - onEachFeature: function (feature, layer) { - let popContent = ''; - for (const key in feature.properties) { - if (feature.properties.hasOwnProperty(key)) { - popContent += '<p>' + key + ': ' + feature.properties[key] + '</p>'; - } - } - layer.bindPopup(popContent); - } - }).addTo(dataGrandLyonMap); - } - }); - } else if (!hasWFS && element.protocol === 'OGC:WMS') { - const crs = new L.Proj.CRS('EPSG:4171', - '+proj=longlat +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +no_defs '); - L.tileLayer.wms('https://download.data.grandlyon.com/wms/grandlyon?', { - crs: crs, - transparent: true, - layers: element.name, - format: 'image/png', - zIndex: 1000 - }).addTo(dataGrandLyonMap); - // source.addSubLayer(element.name); - // source.addTo(dataGrandLyonMap); - } + + + const layerCluster = new olLayerVector({ + source: vectorSource, + style: styleFunction, + zIndex: 1000 + }); + + const modify = new olModify({ source: vectorSource }); + map.addInteraction(modify); + const modifiedFeatures = []; + modify.on('modifyend', function(e) { + const features = e['features'].getArray(); + console.log(features.length); + for (let i = 0; i < features.length; i ++) { + const rev = features[i].getRevision(); + if (rev > 1) { + console.log('feature with revision:' + rev + ' has been modified'); + modifiedFeatures.push(features[i]); + } + } + console.log(modifiedFeatures); }); + map.addLayer(layerCluster); + + const displayFeatureInfo = function (pixel, coordinate) { + const features = []; + map.forEachFeatureAtPixel(pixel, function (feature, layer) { + console.log(feature); + content.innerHTML = '<p>You clicked here:</p><code>' + feature['values_'].identifiant + + '</code>'; + overlay.setPosition(coordinate); + }); + + }; - // results.forEach(element => { - // console.log(element); - - // switch (element.geometry.type) { - // case 'Point': - // const marker = L.marker([element.geometry.coordinates[1], element.geometry.coordinates[0]], { icon: myIcon }) - // .bindPopup(element.properties.nom).addTo(dataGrandLyonMap); - // // group.push(marker); - // cluster.addLayer(marker); - // break; - // case 'Polygon': - // // create a red polygon from an array of LatLng points - // element.geometry.coordinates.forEach(coordinates => { - // coordinates.forEach(coord => { - // coord.reverse(); - // }); - // const polygon = L.polygon(coordinates, { color: 'red' }).addTo(dataGrandLyonMap); - // group.push(polygon); - // }); - - // break; - // } + // map.on('click', function (evt) { + // const coordinate = evt['coordinate']; + // const pixel = evt['pixel']; + // displayFeatureInfo(pixel, coordinate); // }); - // dataGrandLyonMap.addLayer(cluster); - // const featureGroup = L.featureGroup(group); - // // zoom the map to the polygon - // dataGrandLyonMap.fitBounds(featureGroup.getBounds()); - L.control.scale().addTo(dataGrandLyonMap); + + // WMS Functionnality ----------------------- + const wmsSource = new olTileWMS({ + url: 'https://download.data.grandlyon.com/wms/grandlyon?', + params: { 'LAYERS': metadata.uri[0].name, 'TILED': true }, + // Countries have transparency, so do not fade tiles: + // transition: 0 + }); + const layerTile = new olLayerTile({ + source: wmsSource + }); + map.on('click', function (evt) { + + const viewResolution = /** @type {number} */ (map.getView().getResolution()); + console.log(viewResolution); + + const url = wmsSource.getGetFeatureInfoUrl( + evt['coordinate'], viewResolution, 'EPSG:3857', + { 'INFO_FORMAT': 'text/html' }); + console.log(url); + if (url) { + content.innerHTML = '<p>You clicked here:</p><code>' + url + + '</code>'; + + } + }); } onEachFeature(feature, layer) { // does this feature have a property named popupContent? - console.log(feature); - console.log(layer); if (feature.properties && feature.properties.nom) { layer.bindPopup(feature.properties.nom); } diff --git a/webapp/src/assets/leaflet.wms.js b/webapp/src/assets/leaflet.wms.js deleted file mode 100644 index 6a65d604..00000000 --- a/webapp/src/assets/leaflet.wms.js +++ /dev/null @@ -1,495 +0,0 @@ -/*! - * leaflet.wms.js - * A collection of Leaflet utilities for working with Web Mapping services. - * (c) 2014-2016, Houston Engineering, Inc. - * MIT License - */ - -(function (factory) { - // Module systems magic dance, Leaflet edition - if (typeof define === 'function' && define.amd) { - // AMD - define(['leaflet'], factory); - } else if (typeof module !== 'undefined') { - // Node/CommonJS - module.exports = factory(require('leaflet')); - } else { - // Browser globals - if (typeof this.L === 'undefined') - throw 'Leaflet must be loaded first!'; - // Namespace - this.L.WMS = this.L.wms = factory(this.L); - } -}(function (L) { - -// Module object -var wms = {}; - -// Quick shim for Object.keys() -if (!('keys' in Object)) { - Object.keys = function(obj) { - var result = []; - for (var i in obj) { - if (obj.hasOwnProperty(i)) { - result.push(i); - } - } - return result; - }; -} - -/* -* wms.Source -* The Source object manages a single WMS connection. Multiple "layers" can be -* created with the getLayer function, but a single request will be sent for -* each image update. Can be used in non-tiled "overlay" mode (default), or -* tiled mode, via an internal wms.Overlay or wms.TileLayer, respectively. -*/ -wms.Source = L.Layer.extend({ - 'options': { - 'untiled': true, - 'identify': true - }, - - 'initialize': function(url, options) { - L.setOptions(this, options); - if (this.options.tiled) { - this.options.untiled = false; - } - this._url = url; - this._subLayers = {}; - this._overlay = this.createOverlay(this.options.untiled); - }, - - 'createOverlay': function(untiled) { - // Create overlay with all options other than untiled & identify - var overlayOptions = {}; - for (var opt in this.options) { - if (opt != 'untiled' && opt != 'identify') { - overlayOptions[opt] = this.options[opt]; - } - } - if (untiled) { - return wms.overlay(this._url, overlayOptions); - } else { - return wms.tileLayer(this._url, overlayOptions); - } - }, - - 'onAdd': function() { - this.refreshOverlay(); - }, - - 'getEvents': function() { - if (this.options.identify) { - return {'click': this.identify}; - } else { - return {}; - } - }, - - 'setOpacity': function(opacity) { - this.options.opacity = opacity; - if (this._overlay) { - this._overlay.setOpacity(opacity); - } - }, - - 'bringToBack': function() { - this.options.isBack = true; - if (this._overlay) { - this._overlay.bringToBack(); - } - }, - - 'bringToFront': function() { - this.options.isBack = false; - if (this._overlay) { - this._overlay.bringToFront(); - } - }, - - 'getLayer': function(name) { - return wms.layer(this, name); - }, - - 'addSubLayer': function(name) { - this._subLayers[name] = true; - this.refreshOverlay(); - }, - - 'removeSubLayer': function(name) { - delete this._subLayers[name]; - this.refreshOverlay(); - }, - - 'refreshOverlay': function() { - var subLayers = Object.keys(this._subLayers).join(","); - if (!this._map) { - return; - } - if (!subLayers) { - this._overlay.remove(); - } else { - this._overlay.setParams({'layers': subLayers}); - this._overlay.addTo(this._map); - } - }, - - 'identify': function(evt) { - // Identify map features in response to map clicks. To customize this - // behavior, create a class extending wms.Source and override one or - // more of the following hook functions. - - var layers = this.getIdentifyLayers(); - if (!layers.length) { - return; - } - this.getFeatureInfo( - evt.containerPoint, evt.latlng, layers, - this.showFeatureInfo - ); - }, - - 'getFeatureInfo': function(point, latlng, layers, callback) { - // Request WMS GetFeatureInfo and call callback with results - // (split from identify() to faciliate use outside of map events) - var params = this.getFeatureInfoParams(point, layers), - url = this._url + L.Util.getParamString(params, this._url); - - this.showWaiting(); - this.ajax(url, done); - - function done(result) { - this.hideWaiting(); - var text = this.parseFeatureInfo(result, url); - callback.call(this, latlng, text); - } - }, - - 'ajax': function(url, callback) { - ajax.call(this, url, callback); - }, - - 'getIdentifyLayers': function() { - // Hook to determine which layers to identify - if (this.options.identifyLayers) - return this.options.identifyLayers; - return Object.keys(this._subLayers); - }, - - 'getFeatureInfoParams': function(point, layers) { - // Hook to generate parameters for WMS service GetFeatureInfo request - var wmsParams, overlay; - if (this.options.untiled) { - // Use existing overlay - wmsParams = this._overlay.wmsParams; - } else { - // Create overlay instance to leverage updateWmsParams - overlay = this.createOverlay(true); - overlay.updateWmsParams(this._map); - wmsParams = overlay.wmsParams; - wmsParams.layers = layers.join(','); - } - var infoParams = { - 'request': 'GetFeatureInfo', - 'query_layers': layers.join(','), - 'X': Math.round(point.x), - 'Y': Math.round(point.y) - }; - return L.extend({}, wmsParams, infoParams); - }, - - 'parseFeatureInfo': function(result, url) { - // Hook to handle parsing AJAX response - if (result == "error") { - // AJAX failed, possibly due to CORS issues. - // Try loading content in <iframe>. - result = "<iframe src='" + url + "' style='border:none'>"; - } - return result; - }, - - 'showFeatureInfo': function(latlng, info) { - // Hook to handle displaying parsed AJAX response to the user - if (!this._map) { - return; - } - this._map.openPopup(info, latlng); - }, - - 'showWaiting': function() { - // Hook to customize AJAX wait animation - if (!this._map) - return; - this._map._container.style.cursor = "progress"; - }, - - 'hideWaiting': function() { - // Hook to remove AJAX wait animation - if (!this._map) - return; - this._map._container.style.cursor = "default"; - } -}); - -wms.source = function(url, options) { - return new wms.Source(url, options); -}; - -/* -* Layer -* Leaflet "layer" with all actual rendering handled via an underlying Source -* object. Can be called directly with a URL to automatically create or reuse -* an existing Source. Note that the auto-source feature doesn't work well in -* multi-map environments; so for best results, create a Source first and use -* getLayer() to retrieve wms.Layer instances. -*/ - -wms.Layer = L.Layer.extend({ - 'initialize': function(source, layerName, options) { - L.setOptions(this, options); - if (!source.addSubLayer) { - // Assume source is a URL - source = wms.getSourceForUrl(source, options); - } - this._source = source; - this._name = layerName; - }, - 'onAdd': function() { - if (!this._source._map) - this._source.addTo(this._map); - this._source.addSubLayer(this._name); - }, - 'onRemove': function() { - this._source.removeSubLayer(this._name); - }, - 'setOpacity': function(opacity) { - this._source.setOpacity(opacity); - }, - 'bringToBack': function() { - this._source.bringToBack(); - }, - 'bringToFront': function() { - this._source.bringToFront(); - } -}); - -wms.layer = function(source, options) { - return new wms.Layer(source, options); -}; - -// Cache of sources for use with wms.Layer auto-source option -var sources = {}; -wms.getSourceForUrl = function(url, options) { - if (!sources[url]) { - sources[url] = wms.source(url, options); - } - return sources[url]; -}; - - -// Copy tiled WMS layer from leaflet core, in case we need to subclass it later -wms.TileLayer = L.TileLayer.WMS; -wms.tileLayer = L.tileLayer.wms; - -/* -* wms.Overlay: -* "Single Tile" WMS image overlay that updates with map changes. -* Portions of wms.Overlay are directly extracted from L.TileLayer.WMS. -* See Leaflet license. -*/ -wms.Overlay = L.Layer.extend({ - 'defaultWmsParams': { - 'service': 'WMS', - 'request': 'GetMap', - 'version': '1.3.0', - 'layers': '', - 'styles': '', - 'format': 'image/jpeg', - 'transparent': false - }, - - 'options': { - 'crs': null, - 'uppercase': false, - 'attribution': '', - 'opacity': 1, - 'isBack': false, - 'minZoom': 0, - 'maxZoom': 18 - }, - - 'initialize': function(url, options) { - this._url = url; - - // Move WMS parameters to params object - var params = {}, opts = {}; - for (var opt in options) { - if (opt in this.options) { - opts[opt] = options[opt]; - } else { - params[opt] = options[opt]; - } - } - L.setOptions(this, opts); - this.wmsParams = L.extend({}, this.defaultWmsParams, params); - }, - - 'setParams': function(params) { - L.extend(this.wmsParams, params); - this.update(); - }, - - 'getAttribution': function() { - return this.options.attribution; - }, - - 'onAdd': function() { - this.update(); - }, - - 'onRemove': function(map) { - if (this._currentOverlay) { - map.removeLayer(this._currentOverlay); - delete this._currentOverlay; - } - if (this._currentUrl) { - delete this._currentUrl; - } - }, - - 'getEvents': function() { - return { - 'moveend': this.update - }; - }, - - 'update': function() { - if (!this._map) { - return; - } - // Determine image URL and whether it has changed since last update - this.updateWmsParams(); - var url = this.getImageUrl(); - if (this._currentUrl == url) { - return; - } - this._currentUrl = url; - - // Keep current image overlay in place until new one loads - // (inspired by esri.leaflet) - var bounds = this._map.getBounds(); - var overlay = L.imageOverlay(url, bounds, {'opacity': 0}); - overlay.addTo(this._map); - overlay.once('load', _swap, this); - function _swap() { - if (!this._map) { - return; - } - if (overlay._url != this._currentUrl) { - this._map.removeLayer(overlay); - return; - } else if (this._currentOverlay) { - this._map.removeLayer(this._currentOverlay); - } - this._currentOverlay = overlay; - overlay.setOpacity( - this.options.opacity ? this.options.opacity : 1 - ); - if (this.options.isBack === true) { - overlay.bringToBack(); - } - if (this.options.isBack === false) { - overlay.bringToFront(); - } - } - if ((this._map.getZoom() < this.options.minZoom) || - (this._map.getZoom() > this.options.maxZoom)){ - this._map.removeLayer(overlay); - } - }, - - 'setOpacity': function(opacity) { - this.options.opacity = opacity; - if (this._currentOverlay) { - this._currentOverlay.setOpacity(opacity); - } - }, - - 'bringToBack': function() { - this.options.isBack = true; - if (this._currentOverlay) { - this._currentOverlay.bringToBack(); - } - }, - - 'bringToFront': function() { - this.options.isBack = false; - if (this._currentOverlay) { - this._currentOverlay.bringToFront(); - } - }, - - // See L.TileLayer.WMS: onAdd() & getTileUrl() - 'updateWmsParams': function(map) { - if (!map) { - map = this._map; - } - // Compute WMS options - var bounds = map.getBounds(); - var size = map.getSize(); - var wmsVersion = parseFloat(this.wmsParams.version); - var crs = this.options.crs || map.options.crs; - var projectionKey = wmsVersion >= 1.3 ? 'crs' : 'srs'; - var nw = crs.project(bounds.getNorthWest()); - var se = crs.project(bounds.getSouthEast()); - - // Assemble WMS parameter string - var params = { - 'width': size.x, - 'height': size.y - }; - params[projectionKey] = crs.code; - params.bbox = ( - wmsVersion >= 1.3 && crs === L.CRS.EPSG4326 ? - [se.y, nw.x, nw.y, se.x] : - [nw.x, se.y, se.x, nw.y] - ).join(','); - - L.extend(this.wmsParams, params); - }, - - 'getImageUrl': function() { - var uppercase = this.options.uppercase || false; - var pstr = L.Util.getParamString(this.wmsParams, this._url, uppercase); - return this._url + pstr; - } -}); - -wms.overlay = function(url, options) { - return new wms.Overlay(url, options); -}; - -// Simple AJAX helper (since we can't assume jQuery etc. are present) -function ajax(url, callback) { - var context = this, - request = new XMLHttpRequest(); - request.onreadystatechange = change; - request.open('GET', url); - request.send(); - - function change() { - if (request.readyState === 4) { - if (request.status === 200) { - callback.call(context, request.responseText); - } else { - callback.call(context, "error"); - } - } - } -} - -return wms; - -})); diff --git a/webapp/src/tsconfig.app.json b/webapp/src/tsconfig.app.json index edeee7ac..a4ef5de7 100644 --- a/webapp/src/tsconfig.app.json +++ b/webapp/src/tsconfig.app.json @@ -3,12 +3,12 @@ "compilerOptions": { "outDir": "../out-tsc/app", "baseUrl": "./", + "allowSyntheticDefaultImports": true, "allowJs": true, "module": "es2015", "types": [ - "leaflet", - "leaflet.markercluster", - "proj4leaflet" + "ol", + "proj4" ] }, "exclude": [ -- GitLab