@defra/interactive-map 0.0.25-alpha → 0.0.27-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/im-core.js +1 -1
- package/dist/esm/im-shell.js +1 -1
- package/dist/umd/im-core.js +1 -1
- package/dist/umd/index.js +1 -1
- package/docs/api/map-style-config.md +30 -0
- package/docs/assets/css/docusaurus.scss +4 -0
- package/package.json +1 -1
- package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
- package/plugins/beta/draw-ml/src/defaults.js +14 -14
- package/plugins/beta/draw-ml/src/mapboxDraw.js +5 -0
- package/plugins/beta/draw-ml/src/mapboxSnap.js +1 -1
- package/plugins/beta/draw-ml/src/modes/editVertex/touchHandlers.js +10 -2
- package/plugins/beta/draw-ml/src/modes/editVertexMode.js +2 -0
- package/plugins/beta/draw-ml/src/reducer.js +4 -1
- package/plugins/beta/draw-ml/src/styles.js +42 -39
- package/plugins/beta/draw-ol/dist/css/index.css +13 -0
- package/plugins/beta/draw-ol/dist/esm/DrawMode.js +1 -0
- package/plugins/beta/draw-ol/dist/esm/EditMode.js +1 -0
- package/plugins/beta/draw-ol/dist/esm/geometryHelpers.js +1 -0
- package/plugins/beta/draw-ol/dist/esm/im-draw-ol-plugin.js +1 -0
- package/plugins/beta/draw-ol/dist/esm/index.js +1 -0
- package/plugins/beta/draw-ol/src/DrawInit.jsx +67 -0
- package/plugins/beta/draw-ol/src/api/addFeature.js +29 -0
- package/plugins/beta/draw-ol/src/api/deleteFeature.js +11 -0
- package/plugins/beta/draw-ol/src/api/editFeature.js +50 -0
- package/plugins/beta/draw-ol/src/api/newLine.js +44 -0
- package/plugins/beta/draw-ol/src/api/newPolygon.js +44 -0
- package/plugins/beta/draw-ol/src/core/OLDrawManager.js +150 -0
- package/plugins/beta/draw-ol/src/core/featureStore.js +65 -0
- package/plugins/beta/draw-ol/src/core/styles.js +98 -0
- package/plugins/beta/draw-ol/src/core/undoStack.js +31 -0
- package/plugins/beta/draw-ol/src/defaults.js +16 -0
- package/plugins/beta/draw-ol/src/draw/DrawMode.js +113 -0
- package/plugins/beta/draw-ol/src/draw/drawInput.js +199 -0
- package/plugins/beta/draw-ol/src/draw.scss +17 -0
- package/plugins/beta/draw-ol/src/edit/EditMode.js +424 -0
- package/plugins/beta/draw-ol/src/edit/keyboardHandler.js +227 -0
- package/plugins/beta/draw-ol/src/edit/midpointLayer.js +57 -0
- package/plugins/beta/draw-ol/src/edit/touchHandler.js +171 -0
- package/plugins/beta/draw-ol/src/edit/undoOps.js +95 -0
- package/plugins/beta/draw-ol/src/edit/vertexHitTest.js +72 -0
- package/plugins/beta/draw-ol/src/edit/vertexLayer.js +49 -0
- package/plugins/beta/draw-ol/src/edit/vertexOps.js +99 -0
- package/plugins/beta/draw-ol/src/events.js +114 -0
- package/plugins/beta/draw-ol/src/index.js +12 -0
- package/plugins/beta/draw-ol/src/manifest.js +121 -0
- package/plugins/beta/draw-ol/src/olDraw.js +38 -0
- package/plugins/beta/draw-ol/src/reducer.js +41 -0
- package/plugins/beta/draw-ol/src/snap/snapEngine.js +109 -0
- package/plugins/beta/draw-ol/src/snap/snapGeometry.js +200 -0
- package/plugins/beta/draw-ol/src/snap/snapIndicator.js +81 -0
- package/plugins/beta/draw-ol/src/snap/snapInteraction.js +56 -0
- package/plugins/beta/draw-ol/src/snap/snapManager.js +90 -0
- package/plugins/beta/draw-ol/src/utils/flattenStyleProperties.js +47 -0
- package/plugins/beta/draw-ol/src/utils/geometryHelpers.js +97 -0
- package/plugins/beta/draw-ol/src/utils/olCoords.js +35 -0
- package/plugins/beta/draw-ol/src/utils/resolveColors.js +39 -0
- package/plugins/beta/draw-ol/src/utils/spatial.js +28 -0
- package/plugins/beta/draw-ol/src/utils/touchTarget.js +61 -0
- package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
- package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
- package/plugins/interact/src/defaults.js +1 -0
- package/plugins/interact/src/hooks/useInteractionHandlers.js +150 -43
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +240 -8
- package/plugins/interact/src/reducer.js +7 -0
- package/plugins/interact/src/reducer.test.js +16 -0
- package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -1
- package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +1 -1
- package/providers/beta/openlayers/dist/umd/index.js +1 -1
- package/providers/beta/openlayers/src/appEvents.js +9 -13
- package/providers/beta/openlayers/src/appEvents.test.js +27 -18
- package/providers/beta/openlayers/src/mapEvents.js +34 -20
- package/providers/beta/openlayers/src/mapEvents.test.js +65 -6
- package/providers/beta/openlayers/src/openlayersProvider.js +45 -26
- package/providers/beta/openlayers/src/openlayersProvider.test.js +12 -34
- package/providers/beta/openlayers/src/utils/highlightFeatures.js +245 -0
- package/providers/beta/openlayers/src/utils/hoverCursor.js +65 -0
- package/providers/beta/openlayers/src/utils/hoverCursor.test.js +235 -0
- package/providers/beta/openlayers/src/utils/openLayersFixes.js +11 -0
- package/providers/beta/openlayers/src/utils/openLayersFixes.test.js +53 -0
- package/providers/beta/openlayers/src/utils/queryFeatures.js +77 -0
- package/providers/beta/openlayers/src/utils/queryFeatures.test.js +181 -0
- package/providers/beta/openlayers/src/utils/tileLayers.js +23 -5
- package/providers/beta/openlayers/src/utils/tileLayers.test.js +81 -5
- package/providers/beta/openlayers/src/utils/vtTileFragments.js +86 -0
- package/rollup.esm.mjs +6 -0
- package/src/App/controls/keyboardActions.js +2 -2
- package/src/InteractiveMap/InteractiveMap.js +38 -10
- package/src/InteractiveMap/InteractiveMap.test.js +282 -356
- package/src/InteractiveMap/historyManager.js +7 -0
- package/src/InteractiveMap/historyManager.test.js +11 -0
- package/webpack.dev.mjs +3 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! For license information please see index.js.LICENSE.txt */
|
|
2
|
-
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("defra",[],t):"object"==typeof exports?exports.defra=t():(e.defra=e.defra||{},e.defra.openLayersProvider=t())}(this,()=>(()=>{"use strict";var e,t,r={},n={};function o(e){var t=n[e];if(void 0!==t)return t.exports;var i=n[e]={exports:{}};return r[e](i,i.exports,o),i.exports}o.m=r,o.d=(e,t)=>{for(var r in t)o.o(t,r)&&!o.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o.f={},o.e=e=>Promise.all(Object.keys(o.f).reduce((t,r)=>(o.f[r](e,t),t),[])),o.u=e=>"im-openlayers-provider.js",o.miniCssF=e=>{},o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),e={},t="defra.openLayersProvider:",o.l=(r,n,i,a)=>{if(e[r])e[r].push(n);else{var u,c;if(void 0!==i)for(var f=document.getElementsByTagName("script"),p=0;p<f.length;p++){var s=f[p];if(s.getAttribute("src")==r||s.getAttribute("data-webpack")==t+i){u=s;break}}u||(c=!0,(u=document.createElement("script")).charset="utf-8",o.nc&&u.setAttribute("nonce",o.nc),u.setAttribute("data-webpack",t+i),u.src=r),e[r]=[n];var l=(t,n)=>{u.onerror=u.onload=null,clearTimeout(d);var o=e[r];if(delete e[r],u.parentNode&&u.parentNode.removeChild(u),o&&o.forEach(e=>e(n)),t)return t(n)},d=setTimeout(l.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=l.bind(null,u.onerror),u.onload=l.bind(null,u.onload),c&&document.head.appendChild(u)}},o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;o.g.importScripts&&(e=o.g.location+"");var t=o.g.document;if(!e&&t&&(t.currentScript&&"SCRIPT"===t.currentScript.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName("script");if(r.length)for(var n=r.length-1;n>-1&&(!e||!/^http(s?):/.test(e));)e=r[n--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),o.p=e})(),(()=>{var e={57:0};o.f.j=(t,r)=>{var n=o.o(e,t)?e[t]:void 0;if(0!==n)if(n)r.push(n[2]);else{var i=new Promise((r,o)=>n=e[t]=[r,o]);r.push(n[2]=i);var a=o.p+o.u(t),u=new Error;o.l(a,r=>{if(o.o(e,t)&&(0!==(n=e[t])&&(e[t]=void 0),n)){var i=r&&("load"===r.type?"missing":r.type),a=r&&r.target&&r.target.src;u.message="Loading chunk "+t+" failed.\n("+i+": "+a+")",u.name="ChunkLoadError",u.type=i,u.request=a,n[1](u)}},"chunk-"+t,t)}};var t=(t,r)=>{var n,i,[a,u,c]=r,f=0;if(a.some(t=>0!==e[t])){for(n in u)o.o(u,n)&&(o.m[n]=u[n]);c&&c(o)}for(t&&t(r);f<a.length;f++)i=a[f],o.o(e,i)&&e[i]&&e[i][0](),e[i]=0},r=this.webpackChunkdefra_DefraMap=this.webpackChunkdefra_DefraMap||[];r.forEach(t.bind(null,0)),r.push=t.bind(null,r.push.bind(r))})();var i={};function a(){var e,t,r="function"==typeof Symbol?Symbol:{},n=r.iterator||"@@iterator",o=r.toStringTag||"@@toStringTag";function i(r,n,o,i){var a=n&&n.prototype instanceof f?n:f,p=Object.create(a.prototype);return u(p,"_invoke",function(r,n,o){var i,a,u,f=0,p=o||[],s=!1,l={p:0,n:0,v:e,a:d,f:d.bind(e,4),d:function(t,r){return i=t,a=0,u=e,l.n=r,c}};function d(r,n){for(a=r,u=n,t=0;!s&&f&&!o&&t<p.length;t++){var o,i=p[t],d=l.p,v=i[2];r>3?(o=v===n)&&(u=i[(a=i[4])?5:(a=3,3)],i[4]=i[5]=e):i[0]<=d&&((o=r<2&&d<i[1])?(a=0,l.v=n,l.n=i[1]):d<v&&(o=r<3||i[0]>n||n>v)&&(i[4]=r,i[5]=n,l.n=v,a=0))}if(o||r>1)return c;throw s=!0,n}return function(o,p,v){if(f>1)throw TypeError("Generator is already running");for(s&&1===p&&d(p,v),a=p,u=v;(t=a<2?e:u)||!s;){i||(a?a<3?(a>1&&(l.n=-1),d(a,u)):l.n=u:l.v=u);try{if(f=2,i){if(a||(o="next"),t=i[o]){if(!(t=t.call(i,u)))throw TypeError("iterator result is not an object");if(!t.done)return t;u=t.value,a<2&&(a=0)}else 1===a&&(t=i.return)&&t.call(i),a<2&&(u=TypeError("The iterator does not provide a '"+o+"' method"),a=1);i=e}else if((t=(s=l.n<0)?u:r.call(n,l))!==c)break}catch(t){i=e,a=1,u=t}finally{f=1}}return{value:t,done:s}}}(r,o,i),!0),p}var c={};function f(){}function p(){}function s(){}t=Object.getPrototypeOf;var l=[][n]?t(t([][n]())):(u(t={},n,function(){return this}),t),d=s.prototype=f.prototype=Object.create(l);function v(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,s):(e.__proto__=s,u(e,o,"GeneratorFunction")),e.prototype=Object.create(d),e}return p.prototype=s,u(d,"constructor",s),u(s,"constructor",p),p.displayName="GeneratorFunction",u(s,o,"GeneratorFunction"),u(d),u(d,o,"Generator"),u(d,n,function(){return this}),u(d,"toString",function(){return"[object Generator]"}),(a=function(){return{w:i,m:v}})()}function u(e,t,r,n){var o=Object.defineProperty;try{o({},"",{})}catch(e){o=0}u=function(e,t,r,n){function i(t,r){u(e,t,function(e){return this._invoke(t,r,e)})}t?o?o(e,t,{value:r,enumerable:!n,configurable:!n,writable:!n}):e[t]=r:(i("next",0),i("throw",1),i("return",2))},u(e,t,r,n)}function c(e,t,r,n,o,i,a){try{var u=e[i](a),c=u.value}catch(e){return void r(e)}u.done?t(c):Promise.resolve(c).then(n,o)}function f(){var e,t,r=(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}).zoomAlignment;return{checkDeviceCapabilities:function(){return{isSupported:!!document.createElement("canvas").getContext,error:"Canvas is not supported in this browser"}},load:(e=a().m(function e(){var t,n;return a().w(function(e){for(;;)switch(e.n){case 0:return e.n=1,o.e(142).then(o.bind(o,
|
|
2
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("defra",[],t):"object"==typeof exports?exports.defra=t():(e.defra=e.defra||{},e.defra.openLayersProvider=t())}(this,()=>(()=>{"use strict";var e,t,r={},n={};function o(e){var t=n[e];if(void 0!==t)return t.exports;var i=n[e]={exports:{}};return r[e](i,i.exports,o),i.exports}o.m=r,o.d=(e,t)=>{for(var r in t)o.o(t,r)&&!o.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},o.f={},o.e=e=>Promise.all(Object.keys(o.f).reduce((t,r)=>(o.f[r](e,t),t),[])),o.u=e=>"im-openlayers-provider.js",o.miniCssF=e=>{},o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),e={},t="defra.openLayersProvider:",o.l=(r,n,i,a)=>{if(e[r])e[r].push(n);else{var u,c;if(void 0!==i)for(var f=document.getElementsByTagName("script"),p=0;p<f.length;p++){var s=f[p];if(s.getAttribute("src")==r||s.getAttribute("data-webpack")==t+i){u=s;break}}u||(c=!0,(u=document.createElement("script")).charset="utf-8",o.nc&&u.setAttribute("nonce",o.nc),u.setAttribute("data-webpack",t+i),u.src=r),e[r]=[n];var l=(t,n)=>{u.onerror=u.onload=null,clearTimeout(d);var o=e[r];if(delete e[r],u.parentNode&&u.parentNode.removeChild(u),o&&o.forEach(e=>e(n)),t)return t(n)},d=setTimeout(l.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=l.bind(null,u.onerror),u.onload=l.bind(null,u.onload),c&&document.head.appendChild(u)}},o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;o.g.importScripts&&(e=o.g.location+"");var t=o.g.document;if(!e&&t&&(t.currentScript&&"SCRIPT"===t.currentScript.tagName.toUpperCase()&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName("script");if(r.length)for(var n=r.length-1;n>-1&&(!e||!/^http(s?):/.test(e));)e=r[n--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),o.p=e})(),(()=>{var e={57:0};o.f.j=(t,r)=>{var n=o.o(e,t)?e[t]:void 0;if(0!==n)if(n)r.push(n[2]);else{var i=new Promise((r,o)=>n=e[t]=[r,o]);r.push(n[2]=i);var a=o.p+o.u(t),u=new Error;o.l(a,r=>{if(o.o(e,t)&&(0!==(n=e[t])&&(e[t]=void 0),n)){var i=r&&("load"===r.type?"missing":r.type),a=r&&r.target&&r.target.src;u.message="Loading chunk "+t+" failed.\n("+i+": "+a+")",u.name="ChunkLoadError",u.type=i,u.request=a,n[1](u)}},"chunk-"+t,t)}};var t=(t,r)=>{var n,i,[a,u,c]=r,f=0;if(a.some(t=>0!==e[t])){for(n in u)o.o(u,n)&&(o.m[n]=u[n]);c&&c(o)}for(t&&t(r);f<a.length;f++)i=a[f],o.o(e,i)&&e[i]&&e[i][0](),e[i]=0},r=this.webpackChunkdefra_DefraMap=this.webpackChunkdefra_DefraMap||[];r.forEach(t.bind(null,0)),r.push=t.bind(null,r.push.bind(r))})();var i={};function a(){var e,t,r="function"==typeof Symbol?Symbol:{},n=r.iterator||"@@iterator",o=r.toStringTag||"@@toStringTag";function i(r,n,o,i){var a=n&&n.prototype instanceof f?n:f,p=Object.create(a.prototype);return u(p,"_invoke",function(r,n,o){var i,a,u,f=0,p=o||[],s=!1,l={p:0,n:0,v:e,a:d,f:d.bind(e,4),d:function(t,r){return i=t,a=0,u=e,l.n=r,c}};function d(r,n){for(a=r,u=n,t=0;!s&&f&&!o&&t<p.length;t++){var o,i=p[t],d=l.p,v=i[2];r>3?(o=v===n)&&(u=i[(a=i[4])?5:(a=3,3)],i[4]=i[5]=e):i[0]<=d&&((o=r<2&&d<i[1])?(a=0,l.v=n,l.n=i[1]):d<v&&(o=r<3||i[0]>n||n>v)&&(i[4]=r,i[5]=n,l.n=v,a=0))}if(o||r>1)return c;throw s=!0,n}return function(o,p,v){if(f>1)throw TypeError("Generator is already running");for(s&&1===p&&d(p,v),a=p,u=v;(t=a<2?e:u)||!s;){i||(a?a<3?(a>1&&(l.n=-1),d(a,u)):l.n=u:l.v=u);try{if(f=2,i){if(a||(o="next"),t=i[o]){if(!(t=t.call(i,u)))throw TypeError("iterator result is not an object");if(!t.done)return t;u=t.value,a<2&&(a=0)}else 1===a&&(t=i.return)&&t.call(i),a<2&&(u=TypeError("The iterator does not provide a '"+o+"' method"),a=1);i=e}else if((t=(s=l.n<0)?u:r.call(n,l))!==c)break}catch(t){i=e,a=1,u=t}finally{f=1}}return{value:t,done:s}}}(r,o,i),!0),p}var c={};function f(){}function p(){}function s(){}t=Object.getPrototypeOf;var l=[][n]?t(t([][n]())):(u(t={},n,function(){return this}),t),d=s.prototype=f.prototype=Object.create(l);function v(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,s):(e.__proto__=s,u(e,o,"GeneratorFunction")),e.prototype=Object.create(d),e}return p.prototype=s,u(d,"constructor",s),u(s,"constructor",p),p.displayName="GeneratorFunction",u(s,o,"GeneratorFunction"),u(d),u(d,o,"Generator"),u(d,n,function(){return this}),u(d,"toString",function(){return"[object Generator]"}),(a=function(){return{w:i,m:v}})()}function u(e,t,r,n){var o=Object.defineProperty;try{o({},"",{})}catch(e){o=0}u=function(e,t,r,n){function i(t,r){u(e,t,function(e){return this._invoke(t,r,e)})}t?o?o(e,t,{value:r,enumerable:!n,configurable:!n,writable:!n}):e[t]=r:(i("next",0),i("throw",1),i("return",2))},u(e,t,r,n)}function c(e,t,r,n,o,i,a){try{var u=e[i](a),c=u.value}catch(e){return void r(e)}u.done?t(c):Promise.resolve(c).then(n,o)}function f(){var e,t,r=(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}).zoomAlignment;return{checkDeviceCapabilities:function(){return{isSupported:!!document.createElement("canvas").getContext,error:"Canvas is not supported in this browser"}},load:(e=a().m(function e(){var t,n;return a().w(function(e){for(;;)switch(e.n){case 0:return e.n=1,o.e(142).then(o.bind(o,792));case 1:return t=e.v.default,n={zoomAlignment:r,crs:"EPSG:27700"},e.a(2,{MapProvider:t,mapProviderConfig:n})}},e)}),t=function(){var t=this,r=arguments;return new Promise(function(n,o){var i=e.apply(t,r);function a(e){c(i,n,o,a,u,"next",e)}function u(e){c(i,n,o,a,u,"throw",e)}a(void 0)})},function(){return t.apply(this,arguments)})}}return o.d(i,{default:()=>f}),i.default})());
|
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
import { createTileSource, createVectorTileLayer, createOGCVectorTileLayer } from './utils/tileLayers.js'
|
|
1
|
+
import { createTileSource, createVectorTileLayer, createOGCVectorTileLayer, createMapStyleLayer } from './utils/tileLayers.js'
|
|
2
2
|
|
|
3
|
-
export { createTileSource, createVectorTileLayer, createOGCVectorTileLayer }
|
|
3
|
+
export { createTileSource, createVectorTileLayer, createOGCVectorTileLayer, createMapStyleLayer }
|
|
4
4
|
|
|
5
|
-
export function attachAppEvents ({ mapProvider,
|
|
5
|
+
export function attachAppEvents ({ mapProvider, transformRequest, events, eventBus, map, onBaseSourceChange }) {
|
|
6
6
|
const handleSetMapStyle = async (mapStyle) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} else if (mapStyle.type === 'ogc-vt') {
|
|
11
|
-
const { layer: newLayer } = await createOGCVectorTileLayer(mapStyle.url, transformRequest)
|
|
12
|
-
map.getLayers().setAt(0, newLayer)
|
|
13
|
-
} else {
|
|
14
|
-
const { layer: newLayer } = await createVectorTileLayer(mapStyle.url, transformRequest)
|
|
15
|
-
map.getLayers().setAt(0, newLayer)
|
|
16
|
-
}
|
|
7
|
+
const { layer, source } = await createMapStyleLayer(mapStyle, transformRequest)
|
|
8
|
+
map.getLayers().setAt(0, layer)
|
|
9
|
+
onBaseSourceChange(source)
|
|
17
10
|
eventBus.emit(events.MAP_STYLE_CHANGE, { mapStyleId: mapStyle.id })
|
|
11
|
+
// MAP_DATA_CHANGE is driven by the original source's tileloadend and won't fire
|
|
12
|
+
// for the new source, so re-apply highlights directly on the new layer.
|
|
13
|
+
mapProvider.reapplyHighlights()
|
|
18
14
|
}
|
|
19
15
|
|
|
20
16
|
const handleSetPixelRatio = (pixelRatio) => {
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { attachAppEvents } from './appEvents.js'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { createMapStyleLayer } from './utils/tileLayers.js'
|
|
4
4
|
|
|
5
|
+
const mockLayerInstance = {}
|
|
5
6
|
const mockSourceInstance = {}
|
|
6
|
-
const mockVectorTileLayerInstance = {}
|
|
7
7
|
|
|
8
8
|
jest.mock('./utils/tileLayers.js', () => ({
|
|
9
9
|
__esModule: true,
|
|
10
|
-
|
|
11
|
-
createVectorTileLayer: jest.fn(async () => ({ layer: mockVectorTileLayerInstance, source: {} }))
|
|
10
|
+
createMapStyleLayer: jest.fn(async () => ({ layer: mockLayerInstance, source: mockSourceInstance }))
|
|
12
11
|
}))
|
|
13
12
|
|
|
14
13
|
const events = {
|
|
@@ -23,19 +22,21 @@ function makeMap () {
|
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
function makeProvider () {
|
|
26
|
-
return { mapSize: 'small' }
|
|
25
|
+
return { mapSize: 'small', reapplyHighlights: jest.fn() }
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
describe('attachAppEvents', () => {
|
|
30
29
|
describe('raster', () => {
|
|
31
30
|
function makeSetup () {
|
|
32
31
|
jest.clearAllMocks()
|
|
33
|
-
const
|
|
32
|
+
const setAt = jest.fn()
|
|
33
|
+
const map = { getLayers: jest.fn(() => ({ setAt })), setPixelRatio: jest.fn() }
|
|
34
|
+
const onBaseSourceChange = jest.fn()
|
|
34
35
|
const eventBus = { on: jest.fn(), off: jest.fn(), emit: jest.fn() }
|
|
35
36
|
const mapProvider = makeProvider()
|
|
36
|
-
const handles = attachAppEvents({ mapProvider,
|
|
37
|
+
const handles = attachAppEvents({ mapProvider, transformRequest: null, events, eventBus, map, onBaseSourceChange })
|
|
37
38
|
const handlerFor = (event) => eventBus.on.mock.calls.find(c => c[0] === event)[1]
|
|
38
|
-
return {
|
|
39
|
+
return { map, setAt, onBaseSourceChange, eventBus, handles, handlerFor, mapProvider }
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
it('subscribes to MAP_SET_STYLE on the event bus', () => {
|
|
@@ -43,11 +44,18 @@ describe('attachAppEvents', () => {
|
|
|
43
44
|
expect(eventBus.on).toHaveBeenCalledWith(events.MAP_SET_STYLE, expect.any(Function))
|
|
44
45
|
})
|
|
45
46
|
|
|
46
|
-
it('on MAP_SET_STYLE: creates a
|
|
47
|
-
const {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
expect(
|
|
47
|
+
it('on MAP_SET_STYLE: creates a map style layer and places it at index 0', async () => {
|
|
48
|
+
const { setAt, handlerFor } = makeSetup()
|
|
49
|
+
const mapStyle = { url: 'https://new.tiles.com/{z}/{x}/{y}', id: 'newStyle', type: 'raster' }
|
|
50
|
+
await handlerFor(events.MAP_SET_STYLE)(mapStyle)
|
|
51
|
+
expect(createMapStyleLayer).toHaveBeenCalledWith(mapStyle, null)
|
|
52
|
+
expect(setAt).toHaveBeenCalledWith(0, mockLayerInstance)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('on MAP_SET_STYLE: updates the base source for map events', async () => {
|
|
56
|
+
const { onBaseSourceChange, handlerFor } = makeSetup()
|
|
57
|
+
await handlerFor(events.MAP_SET_STYLE)({ url: 'https://new.tiles.com/{z}/{x}/{y}', id: 'newStyle', type: 'raster' })
|
|
58
|
+
expect(onBaseSourceChange).toHaveBeenCalledWith(mockSourceInstance)
|
|
51
59
|
})
|
|
52
60
|
|
|
53
61
|
it('on MAP_SET_STYLE: emits MAP_STYLE_CHANGE with the new style id', async () => {
|
|
@@ -72,16 +80,17 @@ describe('attachAppEvents', () => {
|
|
|
72
80
|
const map = { getLayers: jest.fn(() => ({ setAt })), setPixelRatio: jest.fn() }
|
|
73
81
|
const eventBus = { on: jest.fn(), off: jest.fn(), emit: jest.fn() }
|
|
74
82
|
const mapProvider = makeProvider()
|
|
75
|
-
const handles = attachAppEvents({ mapProvider,
|
|
83
|
+
const handles = attachAppEvents({ mapProvider, transformRequest: null, events, eventBus, map, onBaseSourceChange: jest.fn() })
|
|
76
84
|
const handlerFor = (event) => eventBus.on.mock.calls.find(c => c[0] === event)[1]
|
|
77
85
|
return { map, setAt, eventBus, handles, handlerFor, mapProvider }
|
|
78
86
|
}
|
|
79
87
|
|
|
80
88
|
it('on MAP_SET_STYLE: creates a vector tile layer and places it at index 0', async () => {
|
|
81
89
|
const { setAt, handlerFor } = makeSetup()
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
expect(
|
|
90
|
+
const mapStyle = { url: 'https://example.com/styles', id: 'newVts' }
|
|
91
|
+
await handlerFor(events.MAP_SET_STYLE)(mapStyle)
|
|
92
|
+
expect(createMapStyleLayer).toHaveBeenCalledWith(mapStyle, null)
|
|
93
|
+
expect(setAt).toHaveBeenCalledWith(0, mockLayerInstance)
|
|
85
94
|
})
|
|
86
95
|
|
|
87
96
|
it('on MAP_SET_STYLE: emits MAP_STYLE_CHANGE with the new style id', async () => {
|
|
@@ -97,7 +106,7 @@ describe('attachAppEvents', () => {
|
|
|
97
106
|
const map = makeMap()
|
|
98
107
|
const eventBus = { on: jest.fn(), off: jest.fn(), emit: jest.fn() }
|
|
99
108
|
const mapProvider = makeProvider()
|
|
100
|
-
attachAppEvents({ mapProvider,
|
|
109
|
+
attachAppEvents({ mapProvider, transformRequest: null, events, eventBus, map, onBaseSourceChange: jest.fn() })
|
|
101
110
|
const handlerFor = (event) => eventBus.on.mock.calls.find(c => c[0] === event)[1]
|
|
102
111
|
return { map, mapProvider, handlerFor }
|
|
103
112
|
}
|
|
@@ -6,17 +6,6 @@ const DEBOUNCE_IDLE_TIME = 500
|
|
|
6
6
|
const MOVE_THROTTLE_TIME = 10
|
|
7
7
|
const DRAG_TOLERANCE = 6
|
|
8
8
|
|
|
9
|
-
function attachLoadEvents ({ source, map, emit, eventBus, events, on }) {
|
|
10
|
-
let loadedEmitted = false
|
|
11
|
-
on(source, 'tileloadend', () => {
|
|
12
|
-
if (!loadedEmitted) {
|
|
13
|
-
loadedEmitted = true
|
|
14
|
-
eventBus.emit(events.MAP_LOADED)
|
|
15
|
-
}
|
|
16
|
-
})
|
|
17
|
-
map.once('rendercomplete', () => emit(events.MAP_FIRST_IDLE))
|
|
18
|
-
}
|
|
19
|
-
|
|
20
9
|
function attachMoveEvents ({ map, view, emit, eventBus, events, on, debouncers }) {
|
|
21
10
|
let moving = false
|
|
22
11
|
|
|
@@ -79,11 +68,10 @@ export function attachMapEvents ({
|
|
|
79
68
|
}) {
|
|
80
69
|
let destroyed = false
|
|
81
70
|
const listeners = []
|
|
71
|
+
let sourceListeners = []
|
|
82
72
|
const debouncers = []
|
|
83
73
|
|
|
84
74
|
const view = map.getView()
|
|
85
|
-
const viewMinZoom = view.getMinZoom()
|
|
86
|
-
const viewMaxZoom = view.getMaxZoom()
|
|
87
75
|
|
|
88
76
|
const getMapState = () => {
|
|
89
77
|
if (destroyed) {
|
|
@@ -95,8 +83,8 @@ export function attachMapEvents ({
|
|
|
95
83
|
bounds: getBounds(),
|
|
96
84
|
resolution: getResolution(),
|
|
97
85
|
zoom,
|
|
98
|
-
isAtMaxZoom: zoom + ZOOM_TOLERANCE >=
|
|
99
|
-
isAtMinZoom: zoom - ZOOM_TOLERANCE <=
|
|
86
|
+
isAtMaxZoom: zoom + ZOOM_TOLERANCE >= view.getMaxZoom(),
|
|
87
|
+
isAtMinZoom: zoom - ZOOM_TOLERANCE <= view.getMinZoom()
|
|
100
88
|
}
|
|
101
89
|
}
|
|
102
90
|
|
|
@@ -112,21 +100,47 @@ export function attachMapEvents ({
|
|
|
112
100
|
listeners.push([target, type, handler])
|
|
113
101
|
}
|
|
114
102
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
103
|
+
const onSource = (target, type, handler) => {
|
|
104
|
+
target.on(type, handler)
|
|
105
|
+
sourceListeners.push([target, type, handler])
|
|
106
|
+
}
|
|
118
107
|
|
|
119
|
-
|
|
108
|
+
let loadedEmitted = false
|
|
109
|
+
const emitLoaded = () => {
|
|
110
|
+
if (!loadedEmitted) {
|
|
111
|
+
loadedEmitted = true
|
|
112
|
+
eventBus.emit(events.MAP_LOADED)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
120
115
|
|
|
121
116
|
const emitDataChange = debounce(() => emit(events.MAP_DATA_CHANGE), DEBOUNCE_IDLE_TIME)
|
|
122
117
|
debouncers.push(emitDataChange)
|
|
123
|
-
|
|
118
|
+
|
|
119
|
+
const setSource = (newSource) => {
|
|
120
|
+
if (destroyed) {
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
sourceListeners.forEach(([target, type, handler]) => target.un(type, handler))
|
|
125
|
+
sourceListeners = []
|
|
126
|
+
onSource(newSource, 'tileloadend', emitLoaded)
|
|
127
|
+
onSource(newSource, 'tileloadend', emitDataChange)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
attachMoveEvents({ map, view: map.getView(), emit, eventBus, events, on, debouncers })
|
|
131
|
+
setSource(source)
|
|
132
|
+
map.once('rendercomplete', () => emit(events.MAP_FIRST_IDLE))
|
|
133
|
+
attachClickEvents({ map, eventBus, events, on })
|
|
134
|
+
|
|
135
|
+
on(map, 'postrender', () => eventBus.emit(events.MAP_RENDER))
|
|
124
136
|
|
|
125
137
|
return {
|
|
138
|
+
setSource,
|
|
126
139
|
remove () {
|
|
127
140
|
destroyed = true
|
|
128
141
|
debouncers.forEach(d => d.cancel())
|
|
129
142
|
listeners.forEach(([target, type, handler]) => target.un(type, handler))
|
|
143
|
+
sourceListeners.forEach(([target, type, handler]) => target.un(type, handler))
|
|
130
144
|
}
|
|
131
145
|
}
|
|
132
146
|
}
|
|
@@ -35,7 +35,9 @@ function makeTarget () {
|
|
|
35
35
|
return {
|
|
36
36
|
on: jest.fn((type, handler) => { (handlers[type] = handlers[type] || []).push(handler) }),
|
|
37
37
|
once: jest.fn((type, handler) => { (handlers[type] = handlers[type] || []).push(handler) }),
|
|
38
|
-
un: jest.fn()
|
|
38
|
+
un: jest.fn((type, handler) => {
|
|
39
|
+
handlers[type] = (handlers[type] || []).filter(h => h !== handler)
|
|
40
|
+
}),
|
|
39
41
|
trigger: (type, event = {}) => (handlers[type] || []).forEach(h => h(event))
|
|
40
42
|
}
|
|
41
43
|
}
|
|
@@ -104,14 +106,14 @@ describe('attachMapEvents — move events', () => {
|
|
|
104
106
|
it('emits MAP_MOVE_END (with state) when debounced callback fires', () => {
|
|
105
107
|
const { view, eventBus } = setup()
|
|
106
108
|
view.trigger('change')
|
|
107
|
-
mockDebounceFns[
|
|
109
|
+
mockDebounceFns[1].fn() // emitMoveEnd's inner callback
|
|
108
110
|
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_MOVE_END, expect.any(Object))
|
|
109
111
|
})
|
|
110
112
|
|
|
111
113
|
it('resets moving flag after MAP_MOVE_END so MAP_MOVE_START can fire again', () => {
|
|
112
114
|
const { view, eventBus } = setup()
|
|
113
115
|
view.trigger('change')
|
|
114
|
-
mockDebounceFns[
|
|
116
|
+
mockDebounceFns[1].fn() // resets moving = false
|
|
115
117
|
eventBus.emit.mockClear()
|
|
116
118
|
view.trigger('change')
|
|
117
119
|
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_MOVE_START)
|
|
@@ -168,17 +170,56 @@ describe('attachMapEvents — render and data events', () => {
|
|
|
168
170
|
it('emits MAP_DATA_CHANGE (with state) when debounced tileloadend callback fires', () => {
|
|
169
171
|
const { source, eventBus } = setup()
|
|
170
172
|
source.trigger('tileloadend')
|
|
171
|
-
mockDebounceFns[
|
|
173
|
+
mockDebounceFns[0].fn() // emitDataChange's inner callback
|
|
172
174
|
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_DATA_CHANGE, expect.any(Object))
|
|
173
175
|
})
|
|
176
|
+
|
|
177
|
+
it('moves source listeners when source changes', () => {
|
|
178
|
+
const { source, handles, eventBus } = setup()
|
|
179
|
+
const newSource = makeTarget()
|
|
180
|
+
|
|
181
|
+
handles.setSource(newSource)
|
|
182
|
+
|
|
183
|
+
source.trigger('tileloadend')
|
|
184
|
+
expect(eventBus.emit).not.toHaveBeenCalledWith(events.MAP_LOADED)
|
|
185
|
+
|
|
186
|
+
newSource.trigger('tileloadend')
|
|
187
|
+
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_LOADED)
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
describe('attachMapEvents — dynamic zoom bounds', () => {
|
|
192
|
+
it('isAtMaxZoom is updated in emitted event when view max zoom changes', () => {
|
|
193
|
+
const { view, eventBus } = setup()
|
|
194
|
+
view.trigger('change')
|
|
195
|
+
mockDebounceFns[1].fn()
|
|
196
|
+
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_MOVE_END, expect.objectContaining({ isAtMaxZoom: false }))
|
|
197
|
+
|
|
198
|
+
eventBus.emit.mockClear()
|
|
199
|
+
view.getMaxZoom.mockReturnValue(7)
|
|
200
|
+
mockDebounceFns[1].fn()
|
|
201
|
+
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_MOVE_END, expect.objectContaining({ isAtMaxZoom: true }))
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('isAtMinZoom is updated in emitted event when view min zoom changes', () => {
|
|
205
|
+
const { view, eventBus } = setup()
|
|
206
|
+
view.trigger('change')
|
|
207
|
+
mockDebounceFns[1].fn()
|
|
208
|
+
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_MOVE_END, expect.objectContaining({ isAtMinZoom: false }))
|
|
209
|
+
|
|
210
|
+
eventBus.emit.mockClear()
|
|
211
|
+
view.getMinZoom.mockReturnValue(7)
|
|
212
|
+
mockDebounceFns[1].fn()
|
|
213
|
+
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_MOVE_END, expect.objectContaining({ isAtMinZoom: true }))
|
|
214
|
+
})
|
|
174
215
|
})
|
|
175
216
|
|
|
176
217
|
describe('attachMapEvents — remove', () => {
|
|
177
218
|
it('calls cancel on all debouncers', () => {
|
|
178
219
|
const { handles } = setup()
|
|
179
220
|
handles.remove()
|
|
180
|
-
expect(mockDebounceFns[0].wrapper.cancel).toHaveBeenCalled()
|
|
181
221
|
expect(mockDebounceFns[1].wrapper.cancel).toHaveBeenCalled()
|
|
222
|
+
expect(mockDebounceFns[0].wrapper.cancel).toHaveBeenCalled()
|
|
182
223
|
})
|
|
183
224
|
|
|
184
225
|
it('calls un on all registered event targets', () => {
|
|
@@ -189,10 +230,28 @@ describe('attachMapEvents — remove', () => {
|
|
|
189
230
|
expect(source.un).toHaveBeenCalled()
|
|
190
231
|
})
|
|
191
232
|
|
|
233
|
+
it('unbinds source listeners when source changes', () => {
|
|
234
|
+
const { source, handles } = setup()
|
|
235
|
+
handles.setSource(makeTarget())
|
|
236
|
+
expect(source.un).toHaveBeenCalledWith('tileloadend', expect.any(Function))
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
it('does not bind a new source after remove', () => {
|
|
240
|
+
const { eventBus, handles } = setup()
|
|
241
|
+
const newSource = makeTarget()
|
|
242
|
+
|
|
243
|
+
handles.remove()
|
|
244
|
+
handles.setSource(newSource)
|
|
245
|
+
newSource.trigger('tileloadend')
|
|
246
|
+
|
|
247
|
+
expect(newSource.on).not.toHaveBeenCalled()
|
|
248
|
+
expect(eventBus.emit).not.toHaveBeenCalledWith(events.MAP_LOADED)
|
|
249
|
+
})
|
|
250
|
+
|
|
192
251
|
it('prevents state-bearing events from emitting after destroy', () => {
|
|
193
252
|
const { eventBus, handles } = setup()
|
|
194
253
|
handles.remove()
|
|
195
|
-
mockDebounceFns[
|
|
254
|
+
mockDebounceFns[1].fn() // emitMoveEnd fires after destroy → getMapState returns null
|
|
196
255
|
expect(eventBus.emit).not.toHaveBeenCalledWith(events.MAP_MOVE_END, expect.anything())
|
|
197
256
|
})
|
|
198
257
|
})
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import OlMap from 'ol/Map.js'
|
|
2
2
|
import View from 'ol/View.js'
|
|
3
|
-
import TileLayer from 'ol/layer/Tile.js'
|
|
4
3
|
import { defaults as defaultInteractions } from 'ol/interaction/defaults.js'
|
|
5
4
|
import proj4 from 'proj4'
|
|
6
5
|
import { register } from 'ol/proj/proj4.js'
|
|
7
6
|
import { supportedShortcuts, DEFAULTS } from './defaults.js'
|
|
8
7
|
import { getViewResolutionConfig, ZOOM_ALIGNMENT } from './utils/zoom.js'
|
|
9
8
|
import { attachMapEvents } from './mapEvents.js'
|
|
10
|
-
import { attachAppEvents,
|
|
9
|
+
import { attachAppEvents, createMapStyleLayer } from './appEvents.js'
|
|
11
10
|
import { getAreaDimensions, getCardinalMove, getExtentFromGeoJSON, getPaddedExtent, isGeometryObscured } from './utils/spatial.js'
|
|
11
|
+
import { updateHighlightedFeatures } from './utils/highlightFeatures.js'
|
|
12
|
+
import { queryFeatures } from './utils/queryFeatures.js'
|
|
13
|
+
import { collectTileFragments } from './utils/vtTileFragments.js'
|
|
14
|
+
import { setupHoverCursor } from './utils/hoverCursor.js'
|
|
15
|
+
import { applyOpenLayersFixes } from './utils/openLayersFixes.js'
|
|
16
|
+
|
|
17
|
+
applyOpenLayersFixes()
|
|
12
18
|
|
|
13
19
|
const CRS = 'EPSG:27700'
|
|
14
20
|
|
|
15
|
-
// OL view padding is [top, right, bottom, left]; app passes { top, right, bottom, left }
|
|
16
21
|
const toPaddingArray = (padding) => {
|
|
17
22
|
if (!padding) {
|
|
18
23
|
return undefined
|
|
@@ -42,19 +47,7 @@ export default class OpenLayersProvider {
|
|
|
42
47
|
this.mapSize = mapSize
|
|
43
48
|
const { events, eventBus } = this
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
if (mapStyle.type === 'raster') {
|
|
47
|
-
source = createTileSource(mapStyle.url, transformRequest)
|
|
48
|
-
tileLayer = new TileLayer({ source })
|
|
49
|
-
} else if (mapStyle.type === 'ogc-vt') {
|
|
50
|
-
const vectorTile = await createOGCVectorTileLayer(mapStyle.url, transformRequest)
|
|
51
|
-
tileLayer = vectorTile.layer
|
|
52
|
-
source = vectorTile.source
|
|
53
|
-
} else {
|
|
54
|
-
const vectorTile = await createVectorTileLayer(mapStyle.url, transformRequest)
|
|
55
|
-
tileLayer = vectorTile.layer
|
|
56
|
-
source = vectorTile.source
|
|
57
|
-
}
|
|
50
|
+
const { layer: tileLayer, source } = await createMapStyleLayer(mapStyle, transformRequest)
|
|
58
51
|
|
|
59
52
|
const viewResolutions = getViewResolutionConfig(this.zoomAlignment ?? ZOOM_ALIGNMENT.UK)
|
|
60
53
|
|
|
@@ -97,18 +90,15 @@ export default class OpenLayersProvider {
|
|
|
97
90
|
|
|
98
91
|
this.appEventHandles = attachAppEvents({
|
|
99
92
|
mapProvider: this,
|
|
100
|
-
layer: tileLayer,
|
|
101
|
-
layerType: mapStyle.type ?? 'vector',
|
|
102
93
|
transformRequest,
|
|
103
94
|
events,
|
|
104
95
|
eventBus,
|
|
105
|
-
map
|
|
96
|
+
map,
|
|
97
|
+
onBaseSourceChange: this.mapEventHandles.setSource
|
|
106
98
|
}) || []
|
|
107
99
|
|
|
108
100
|
this.map = map
|
|
109
101
|
this.view = view
|
|
110
|
-
this.tileLayer = tileLayer
|
|
111
|
-
this.source = source
|
|
112
102
|
|
|
113
103
|
// MAP_READY is synchronous — OL map is immediately interactive after construction
|
|
114
104
|
eventBus.emit(events.MAP_READY, {
|
|
@@ -127,13 +117,15 @@ export default class OpenLayersProvider {
|
|
|
127
117
|
this.appEventHandles = null
|
|
128
118
|
|
|
129
119
|
if (this.map) {
|
|
120
|
+
if (this._onHoverMove) {
|
|
121
|
+
this.map.un('pointermove', this._onHoverMove)
|
|
122
|
+
this._onHoverMove = null
|
|
123
|
+
}
|
|
130
124
|
this.map.setTarget(null)
|
|
131
125
|
this.map = null
|
|
132
126
|
}
|
|
133
127
|
|
|
134
128
|
this.view = null
|
|
135
|
-
this.tileLayer = null
|
|
136
|
-
this.source = null
|
|
137
129
|
}
|
|
138
130
|
|
|
139
131
|
// ==========================
|
|
@@ -203,15 +195,42 @@ export default class OpenLayersProvider {
|
|
|
203
195
|
return extent.map(n => Math.round(n * 100) / 100)
|
|
204
196
|
}
|
|
205
197
|
|
|
206
|
-
getFeaturesAtPoint (
|
|
207
|
-
|
|
208
|
-
|
|
198
|
+
getFeaturesAtPoint (point, options) {
|
|
199
|
+
return queryFeatures(this.map, point, options)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
getFeatureGeometry (layerId, featureId, idProperty) {
|
|
203
|
+
const fragments = collectTileFragments(this.map, layerId, featureId, idProperty)
|
|
204
|
+
if (fragments.length === 0) { return null }
|
|
205
|
+
if (fragments.length === 1) { return fragments[0] }
|
|
206
|
+
const coords = fragments.flatMap(g => g.type === 'MultiPolygon' ? g.coordinates : [g.coordinates])
|
|
207
|
+
return { type: 'MultiPolygon', coordinates: coords }
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
setHoverCursor (layerIds) {
|
|
211
|
+
if (!this.map) {
|
|
212
|
+
return
|
|
213
|
+
}
|
|
214
|
+
this._onHoverMove = setupHoverCursor(this.map, layerIds, this._onHoverMove)
|
|
209
215
|
}
|
|
210
216
|
|
|
211
217
|
getVisibleFeatures (_layerIds) {
|
|
212
218
|
return []
|
|
213
219
|
}
|
|
214
220
|
|
|
221
|
+
updateHighlightedFeatures (selectedFeatures, activeFeatures, stylesMap) {
|
|
222
|
+
this._lastHighlightArgs = { selectedFeatures, activeFeatures, stylesMap }
|
|
223
|
+
return updateHighlightedFeatures(this.map, selectedFeatures, activeFeatures, stylesMap)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
reapplyHighlights () {
|
|
227
|
+
if (!this._lastHighlightArgs) {
|
|
228
|
+
return
|
|
229
|
+
}
|
|
230
|
+
const { selectedFeatures, activeFeatures, stylesMap } = this._lastHighlightArgs
|
|
231
|
+
updateHighlightedFeatures(this.map, selectedFeatures, activeFeatures, stylesMap)
|
|
232
|
+
}
|
|
233
|
+
|
|
215
234
|
// ==========================
|
|
216
235
|
// Spatial helpers
|
|
217
236
|
// ==========================
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Mock-prefixed variables are allowed in jest.mock factories by babel-plugin-jest-hoist
|
|
2
2
|
import OpenLayersProvider from './openlayersProvider.js'
|
|
3
3
|
import { attachMapEvents } from './mapEvents.js'
|
|
4
|
-
import { attachAppEvents,
|
|
4
|
+
import { attachAppEvents, createMapStyleLayer } from './appEvents.js'
|
|
5
5
|
import { getExtentFromGeoJSON, isGeometryObscured } from './utils/spatial.js'
|
|
6
6
|
|
|
7
7
|
const mockAnimate = jest.fn()
|
|
@@ -35,14 +35,13 @@ const mockMapInstance = {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
const mockSource = {}
|
|
38
|
-
const mockTileLayer = { setSource: jest.fn() }
|
|
39
38
|
const mockVectorTileLayer = {}
|
|
40
39
|
const mockMapEventHandlesRemove = jest.fn()
|
|
40
|
+
const mockSetSource = jest.fn()
|
|
41
41
|
const mockAppEventHandlesRemove = jest.fn()
|
|
42
42
|
|
|
43
43
|
jest.mock('ol/Map.js', () => ({ __esModule: true, default: jest.fn(() => mockMapInstance) }))
|
|
44
44
|
jest.mock('ol/View.js', () => ({ __esModule: true, default: jest.fn(() => mockViewInstance) }))
|
|
45
|
-
jest.mock('ol/layer/Tile.js', () => ({ __esModule: true, default: jest.fn(() => mockTileLayer) }))
|
|
46
45
|
jest.mock('ol/interaction/defaults.js', () => ({ __esModule: true, defaults: jest.fn(() => []) }))
|
|
47
46
|
jest.mock('proj4', () => {
|
|
48
47
|
const fn = jest.fn()
|
|
@@ -52,12 +51,11 @@ jest.mock('proj4', () => {
|
|
|
52
51
|
jest.mock('ol/proj/proj4.js', () => ({ __esModule: true, register: jest.fn() }))
|
|
53
52
|
jest.mock('./mapEvents.js', () => ({
|
|
54
53
|
__esModule: true,
|
|
55
|
-
attachMapEvents: jest.fn(() => ({ remove: mockMapEventHandlesRemove }))
|
|
54
|
+
attachMapEvents: jest.fn(() => ({ remove: mockMapEventHandlesRemove, setSource: mockSetSource }))
|
|
56
55
|
}))
|
|
57
56
|
jest.mock('./appEvents.js', () => ({
|
|
58
57
|
__esModule: true,
|
|
59
|
-
|
|
60
|
-
createVectorTileLayer: jest.fn(async () => ({ layer: mockVectorTileLayer, source: mockSource })),
|
|
58
|
+
createMapStyleLayer: jest.fn(async () => ({ layer: mockVectorTileLayer, source: mockSource })),
|
|
61
59
|
attachAppEvents: jest.fn(() => ({ remove: mockAppEventHandlesRemove }))
|
|
62
60
|
}))
|
|
63
61
|
jest.mock('./utils/spatial.js', () => ({
|
|
@@ -125,7 +123,7 @@ describe('OpenLayersProvider', () => {
|
|
|
125
123
|
it('creates vector tile layer, OL objects, and emits MAP_READY by default', async () => {
|
|
126
124
|
const { provider, eventBus } = makeProvider()
|
|
127
125
|
await provider.initMap(defaultInitConfig)
|
|
128
|
-
expect(
|
|
126
|
+
expect(createMapStyleLayer).toHaveBeenCalledWith(defaultInitConfig.mapStyle, null)
|
|
129
127
|
expect(attachMapEvents).toHaveBeenCalled()
|
|
130
128
|
expect(attachAppEvents).toHaveBeenCalled()
|
|
131
129
|
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_READY, expect.objectContaining({
|
|
@@ -151,43 +149,29 @@ describe('OpenLayersProvider', () => {
|
|
|
151
149
|
expect(mockMapOnce).not.toHaveBeenCalled()
|
|
152
150
|
})
|
|
153
151
|
|
|
154
|
-
it('
|
|
152
|
+
it('creates the initial layer from the map style', async () => {
|
|
155
153
|
const { provider } = makeProvider()
|
|
156
154
|
await provider.initMap(defaultInitConfig)
|
|
157
|
-
expect(
|
|
158
|
-
expect(createTileSource).not.toHaveBeenCalled()
|
|
155
|
+
expect(createMapStyleLayer).toHaveBeenCalledWith(defaultInitConfig.mapStyle, null)
|
|
159
156
|
})
|
|
160
157
|
|
|
161
|
-
it('
|
|
158
|
+
it('creates the initial layer from raster map styles', async () => {
|
|
162
159
|
const { provider, eventBus } = makeProvider()
|
|
163
160
|
const rasterConfig = {
|
|
164
161
|
...defaultInitConfig,
|
|
165
162
|
mapStyle: { id: 'myStyle', url: 'https://tiles.example.com/{z}/{x}/{y}', type: 'raster' }
|
|
166
163
|
}
|
|
167
164
|
await provider.initMap(rasterConfig)
|
|
168
|
-
expect(
|
|
169
|
-
expect(createVectorTileLayer).not.toHaveBeenCalled()
|
|
165
|
+
expect(createMapStyleLayer).toHaveBeenCalledWith(rasterConfig.mapStyle, null)
|
|
170
166
|
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_READY, expect.objectContaining({ mapStyleId: 'myStyle' }))
|
|
171
167
|
})
|
|
172
168
|
|
|
173
|
-
it('passes
|
|
169
|
+
it('passes map and source change handler to attachAppEvents', async () => {
|
|
174
170
|
const { provider } = makeProvider()
|
|
175
171
|
await provider.initMap(defaultInitConfig)
|
|
176
172
|
expect(attachAppEvents).toHaveBeenCalledWith(expect.objectContaining({
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}))
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
it('passes layerType "raster" to attachAppEvents when mapStyle.type is "raster"', async () => {
|
|
183
|
-
const { provider } = makeProvider()
|
|
184
|
-
const rasterConfig = {
|
|
185
|
-
...defaultInitConfig,
|
|
186
|
-
mapStyle: { id: 'myStyle', url: 'https://tiles.example.com/{z}/{x}/{y}', type: 'raster' }
|
|
187
|
-
}
|
|
188
|
-
await provider.initMap(rasterConfig)
|
|
189
|
-
expect(attachAppEvents).toHaveBeenCalledWith(expect.objectContaining({
|
|
190
|
-
layerType: 'raster'
|
|
173
|
+
map: mockMapInstance,
|
|
174
|
+
onBaseSourceChange: mockSetSource
|
|
191
175
|
}))
|
|
192
176
|
})
|
|
193
177
|
|
|
@@ -218,8 +202,6 @@ describe('OpenLayersProvider', () => {
|
|
|
218
202
|
expect(mockMapSetTarget).toHaveBeenCalledWith(null)
|
|
219
203
|
expect(provider.map).toBeNull()
|
|
220
204
|
expect(provider.view).toBeNull()
|
|
221
|
-
expect(provider.tileLayer).toBeNull()
|
|
222
|
-
expect(provider.source).toBeNull()
|
|
223
205
|
})
|
|
224
206
|
|
|
225
207
|
it('does not throw when called before initMap', () => {
|
|
@@ -324,10 +306,6 @@ describe('OpenLayersProvider', () => {
|
|
|
324
306
|
expect(provider.getBounds()).toEqual([1.13, 2.46, 3.99, 4])
|
|
325
307
|
})
|
|
326
308
|
|
|
327
|
-
it('getFeaturesAtPoint returns empty array', () => {
|
|
328
|
-
expect(provider.getFeaturesAtPoint()).toEqual([])
|
|
329
|
-
})
|
|
330
|
-
|
|
331
309
|
it('getVisibleFeatures returns empty array', () => {
|
|
332
310
|
expect(provider.getVisibleFeatures()).toEqual([])
|
|
333
311
|
})
|