@defra/interactive-map 0.0.26-alpha → 0.0.28-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/css/index.css +1 -1
- 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/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/beta/map-styles/dist/esm/im-map-styles-plugin.js +1 -1
- package/plugins/beta/map-styles/dist/umd/im-map-styles-plugin.js +1 -1
- 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 +25 -17
- package/providers/beta/openlayers/src/mapEvents.js +32 -16
- package/providers/beta/openlayers/src/mapEvents.test.js +43 -10
- 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 +19 -1
- package/providers/beta/openlayers/src/utils/tileLayers.test.js +74 -3
- package/providers/beta/openlayers/src/utils/vtTileFragments.js +86 -0
- package/rollup.esm.mjs +6 -0
- package/src/App/components/Hints/Hints.test.jsx +2 -2
- package/src/App/components/PopupMenu/PopupMenu.module.scss +6 -0
- package/src/App/controls/keyboardActions.js +2 -2
- package/src/App/controls/keyboardMappings.js +1 -2
- package/src/App/controls/keyboardShortcuts.js +5 -3
- package/src/App/controls/keyboardShortcuts.test.js +24 -0
- package/src/InteractiveMap/InteractiveMap.js +38 -10
- package/src/InteractiveMap/InteractiveMap.test.js +282 -356
- package/src/InteractiveMap/deviceChecker.js +3 -2
- package/src/InteractiveMap/deviceChecker.test.js +7 -10
- package/src/InteractiveMap/historyManager.js +7 -0
- package/src/InteractiveMap/historyManager.test.js +11 -0
- package/src/config/defaults.js +1 -1
- package/src/utils/htmlToPlainText.js +11 -0
- package/src/utils/htmlToPlainText.test.js +5 -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, mapStyle)
|
|
12
|
-
map.getLayers().setAt(0, newLayer)
|
|
13
|
-
} else {
|
|
14
|
-
const { layer: newLayer } = await createVectorTileLayer(mapStyle.url, transformRequest, mapStyle)
|
|
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,7 +80,7 @@ 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
|
}
|
|
@@ -81,8 +89,8 @@ describe('attachAppEvents', () => {
|
|
|
81
89
|
const { setAt, handlerFor } = makeSetup()
|
|
82
90
|
const mapStyle = { url: 'https://example.com/styles', id: 'newVts' }
|
|
83
91
|
await handlerFor(events.MAP_SET_STYLE)(mapStyle)
|
|
84
|
-
expect(
|
|
85
|
-
expect(setAt).toHaveBeenCalledWith(0,
|
|
92
|
+
expect(createMapStyleLayer).toHaveBeenCalledWith(mapStyle, null)
|
|
93
|
+
expect(setAt).toHaveBeenCalledWith(0, mockLayerInstance)
|
|
86
94
|
})
|
|
87
95
|
|
|
88
96
|
it('on MAP_SET_STYLE: emits MAP_STYLE_CHANGE with the new style id', async () => {
|
|
@@ -98,7 +106,7 @@ describe('attachAppEvents', () => {
|
|
|
98
106
|
const map = makeMap()
|
|
99
107
|
const eventBus = { on: jest.fn(), off: jest.fn(), emit: jest.fn() }
|
|
100
108
|
const mapProvider = makeProvider()
|
|
101
|
-
attachAppEvents({ mapProvider,
|
|
109
|
+
attachAppEvents({ mapProvider, transformRequest: null, events, eventBus, map, onBaseSourceChange: jest.fn() })
|
|
102
110
|
const handlerFor = (event) => eventBus.on.mock.calls.find(c => c[0] === event)[1]
|
|
103
111
|
return { map, mapProvider, handlerFor }
|
|
104
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,6 +68,7 @@ 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()
|
|
@@ -110,21 +100,47 @@ export function attachMapEvents ({
|
|
|
110
100
|
listeners.push([target, type, handler])
|
|
111
101
|
}
|
|
112
102
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
103
|
+
const onSource = (target, type, handler) => {
|
|
104
|
+
target.on(type, handler)
|
|
105
|
+
sourceListeners.push([target, type, handler])
|
|
106
|
+
}
|
|
116
107
|
|
|
117
|
-
|
|
108
|
+
let loadedEmitted = false
|
|
109
|
+
const emitLoaded = () => {
|
|
110
|
+
if (!loadedEmitted) {
|
|
111
|
+
loadedEmitted = true
|
|
112
|
+
eventBus.emit(events.MAP_LOADED)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
118
115
|
|
|
119
116
|
const emitDataChange = debounce(() => emit(events.MAP_DATA_CHANGE), DEBOUNCE_IDLE_TIME)
|
|
120
117
|
debouncers.push(emitDataChange)
|
|
121
|
-
|
|
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))
|
|
122
136
|
|
|
123
137
|
return {
|
|
138
|
+
setSource,
|
|
124
139
|
remove () {
|
|
125
140
|
destroyed = true
|
|
126
141
|
debouncers.forEach(d => d.cancel())
|
|
127
142
|
listeners.forEach(([target, type, handler]) => target.un(type, handler))
|
|
143
|
+
sourceListeners.forEach(([target, type, handler]) => target.un(type, handler))
|
|
128
144
|
}
|
|
129
145
|
}
|
|
130
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,33 +170,46 @@ 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
|
+
})
|
|
174
189
|
})
|
|
175
190
|
|
|
176
191
|
describe('attachMapEvents — dynamic zoom bounds', () => {
|
|
177
192
|
it('isAtMaxZoom is updated in emitted event when view max zoom changes', () => {
|
|
178
193
|
const { view, eventBus } = setup()
|
|
179
194
|
view.trigger('change')
|
|
180
|
-
mockDebounceFns[
|
|
195
|
+
mockDebounceFns[1].fn()
|
|
181
196
|
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_MOVE_END, expect.objectContaining({ isAtMaxZoom: false }))
|
|
182
197
|
|
|
183
198
|
eventBus.emit.mockClear()
|
|
184
199
|
view.getMaxZoom.mockReturnValue(7)
|
|
185
|
-
mockDebounceFns[
|
|
200
|
+
mockDebounceFns[1].fn()
|
|
186
201
|
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_MOVE_END, expect.objectContaining({ isAtMaxZoom: true }))
|
|
187
202
|
})
|
|
188
203
|
|
|
189
204
|
it('isAtMinZoom is updated in emitted event when view min zoom changes', () => {
|
|
190
205
|
const { view, eventBus } = setup()
|
|
191
206
|
view.trigger('change')
|
|
192
|
-
mockDebounceFns[
|
|
207
|
+
mockDebounceFns[1].fn()
|
|
193
208
|
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_MOVE_END, expect.objectContaining({ isAtMinZoom: false }))
|
|
194
209
|
|
|
195
210
|
eventBus.emit.mockClear()
|
|
196
211
|
view.getMinZoom.mockReturnValue(7)
|
|
197
|
-
mockDebounceFns[
|
|
212
|
+
mockDebounceFns[1].fn()
|
|
198
213
|
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_MOVE_END, expect.objectContaining({ isAtMinZoom: true }))
|
|
199
214
|
})
|
|
200
215
|
})
|
|
@@ -203,8 +218,8 @@ describe('attachMapEvents — remove', () => {
|
|
|
203
218
|
it('calls cancel on all debouncers', () => {
|
|
204
219
|
const { handles } = setup()
|
|
205
220
|
handles.remove()
|
|
206
|
-
expect(mockDebounceFns[0].wrapper.cancel).toHaveBeenCalled()
|
|
207
221
|
expect(mockDebounceFns[1].wrapper.cancel).toHaveBeenCalled()
|
|
222
|
+
expect(mockDebounceFns[0].wrapper.cancel).toHaveBeenCalled()
|
|
208
223
|
})
|
|
209
224
|
|
|
210
225
|
it('calls un on all registered event targets', () => {
|
|
@@ -215,10 +230,28 @@ describe('attachMapEvents — remove', () => {
|
|
|
215
230
|
expect(source.un).toHaveBeenCalled()
|
|
216
231
|
})
|
|
217
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
|
+
|
|
218
251
|
it('prevents state-bearing events from emitting after destroy', () => {
|
|
219
252
|
const { eventBus, handles } = setup()
|
|
220
253
|
handles.remove()
|
|
221
|
-
mockDebounceFns[
|
|
254
|
+
mockDebounceFns[1].fn() // emitMoveEnd fires after destroy → getMapState returns null
|
|
222
255
|
expect(eventBus.emit).not.toHaveBeenCalledWith(events.MAP_MOVE_END, expect.anything())
|
|
223
256
|
})
|
|
224
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, mapStyle)
|
|
51
|
-
tileLayer = vectorTile.layer
|
|
52
|
-
source = vectorTile.source
|
|
53
|
-
} else {
|
|
54
|
-
const vectorTile = await createVectorTileLayer(mapStyle.url, transformRequest, mapStyle)
|
|
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
|
})
|