@defra/interactive-map 0.0.17-alpha → 0.0.19-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/assets/css/docusaurus.css +58 -34
- 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/api/context.md +53 -7
- package/docs/api/map-style-config.md +41 -2
- package/docs/api/marker-config.md +53 -11
- package/docs/api/panel-definition.md +16 -0
- package/docs/api/symbol-config.md +160 -0
- package/docs/api/symbol-registry.md +115 -0
- package/docs/api.md +50 -23
- package/docs/assets/basic-map.jpg +0 -0
- package/docs/assets/button-first.jpg +0 -0
- package/docs/assets/maker-panel.jpg +0 -0
- package/docs/examples/add-marker-with-panel.mdx +59 -0
- package/docs/examples/basic-map.mdx +24 -0
- package/docs/examples/button-map.mdx +24 -0
- package/docs/examples/index.mdx +49 -0
- package/docs/index.mdx +1 -1
- package/docs/plugins/datasets.md +105 -9
- package/docs/plugins/interact.md +100 -44
- package/docs/plugins/search.md +15 -3
- package/docs/plugins.md +1 -1
- package/docusaurus.config.cjs +9 -1
- package/package.json +1 -1
- package/plugins/beta/datasets/dist/css/index.css +32 -14
- package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/esm/index.js +1 -1
- package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +1 -1
- package/plugins/beta/datasets/dist/umd/index.js +1 -1
- package/plugins/beta/datasets/src/DatasetsInit.jsx +9 -4
- package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +57 -11
- package/plugins/beta/datasets/src/adapters/maplibre/layerIds.js +14 -8
- package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +155 -53
- package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +27 -0
- package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +31 -0
- package/plugins/beta/datasets/src/api/addDataset.js +1 -1
- package/plugins/beta/datasets/src/api/setData.js +4 -2
- package/plugins/beta/datasets/src/api/setStyle.js +2 -2
- package/plugins/beta/datasets/src/components/EmptyKey.jsx +7 -0
- package/plugins/beta/datasets/src/components/EmptyKey.test.jsx +21 -0
- package/plugins/beta/datasets/src/components/KeySvg.jsx +24 -0
- package/plugins/beta/datasets/src/components/KeySvgLine.jsx +19 -0
- package/plugins/beta/datasets/src/components/KeySvgPattern.jsx +15 -0
- package/plugins/beta/datasets/src/components/KeySvgRect.jsx +22 -0
- package/plugins/beta/datasets/src/components/KeySvgSymbol.jsx +16 -0
- package/plugins/beta/datasets/src/components/svgProperties.js +20 -0
- package/plugins/beta/datasets/src/datasets.js +13 -4
- package/plugins/beta/datasets/src/defaults.js +4 -2
- package/plugins/beta/datasets/src/index.js +2 -1
- package/plugins/beta/datasets/src/manifest.js +1 -1
- package/plugins/beta/datasets/src/panels/Key.jsx +11 -89
- package/plugins/beta/datasets/src/panels/Key.module.scss +24 -13
- package/plugins/beta/datasets/src/panels/Layers.module.scss +13 -7
- package/plugins/beta/datasets/src/reducer.js +6 -0
- package/plugins/beta/datasets/src/reducers/keyReducer.js +34 -0
- package/plugins/beta/datasets/src/utils/mergeSublayer.js +8 -0
- package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
- package/plugins/beta/draw-es/src/DrawInit.jsx +3 -2
- package/plugins/beta/draw-ml/dist/css/index.css +3 -0
- 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/dist/umd/index.js +1 -1
- package/plugins/beta/draw-ml/src/DrawInit.jsx +4 -3
- 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/beta/map-styles/dist/umd/index.js +1 -1
- package/plugins/beta/map-styles/src/MapStyles.jsx +5 -4
- package/plugins/beta/map-styles/src/MapStylesInit.jsx +5 -4
- package/plugins/beta/scale-bar/dist/css/index.css +1 -1
- package/plugins/beta/scale-bar/src/scaleBar.scss +1 -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/dist/umd/index.js +1 -1
- package/plugins/interact/src/InteractInit.jsx +19 -8
- package/plugins/interact/src/InteractInit.test.js +26 -6
- package/plugins/interact/src/api/clear.js +1 -1
- package/plugins/interact/src/api/enable.test.js +7 -7
- package/plugins/interact/src/api/selectMarker.js +14 -0
- package/plugins/interact/src/api/selectMarker.test.js +25 -0
- package/plugins/interact/src/api/unselectMarker.js +14 -0
- package/plugins/interact/src/api/unselectMarker.test.js +14 -0
- package/plugins/interact/src/defaults.js +4 -6
- package/plugins/interact/src/events.js +27 -36
- package/plugins/interact/src/events.test.js +119 -90
- package/plugins/interact/src/hooks/useHighlightSync.js +3 -3
- package/plugins/interact/src/hooks/useHighlightSync.test.js +6 -6
- package/plugins/interact/src/hooks/useHoverCursor.js +10 -0
- package/plugins/interact/src/hooks/useHoverCursor.test.js +44 -0
- package/plugins/interact/src/hooks/useInteractionHandlers.js +111 -69
- package/plugins/interact/src/hooks/useInteractionHandlers.test.js +147 -32
- package/plugins/interact/src/manifest.js +10 -2
- package/plugins/interact/src/reducer.js +59 -5
- package/plugins/interact/src/reducer.test.js +100 -12
- package/plugins/interact/src/utils/buildStylesMap.js +17 -4
- package/plugins/interact/src/utils/buildStylesMap.test.js +16 -2
- package/plugins/interact/src/utils/featureQueries.js +11 -6
- package/plugins/interact/src/utils/featureQueries.test.js +8 -1
- package/plugins/interact/src/utils/interactionModes.js +12 -0
- package/plugins/search/dist/esm/im-search-plugin.js +1 -1
- package/plugins/search/dist/umd/im-search-plugin.js +1 -1
- package/plugins/search/src/Search.jsx +3 -1
- package/plugins/search/src/events/fetchSuggestions.js +6 -4
- package/plugins/search/src/events/fetchSuggestions.test.js +26 -4
- package/plugins/search/src/events/formHandlers.js +3 -3
- package/plugins/search/src/events/formHandlers.test.js +1 -1
- package/plugins/search/src/events/suggestionHandlers.js +2 -2
- package/plugins/search/src/events/suggestionHandlers.test.js +1 -1
- package/plugins/search/src/utils/updateMap.js +3 -3
- package/plugins/search/src/utils/updateMap.test.js +3 -3
- package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
- package/providers/maplibre/dist/umd/index.js +1 -1
- package/providers/maplibre/src/appEvents.js +7 -0
- package/providers/maplibre/src/appEvents.test.js +18 -4
- package/providers/maplibre/src/maplibreProvider.js +52 -0
- package/providers/maplibre/src/maplibreProvider.test.js +105 -1
- package/providers/maplibre/src/utils/highlightFeatures.js +36 -7
- package/providers/maplibre/src/utils/highlightFeatures.test.js +153 -96
- package/providers/maplibre/src/utils/hoverCursor.js +61 -0
- package/providers/maplibre/src/utils/hoverCursor.test.js +130 -0
- package/providers/maplibre/src/utils/patternImages.js +70 -0
- package/providers/maplibre/src/utils/patternImages.test.js +180 -0
- package/providers/maplibre/src/utils/queryFeatures.js +38 -16
- package/providers/maplibre/src/utils/queryFeatures.test.js +20 -3
- package/providers/maplibre/src/utils/rasteriseToImageData.js +30 -0
- package/providers/maplibre/src/utils/rasteriseToImageData.test.js +69 -0
- package/providers/maplibre/src/utils/symbolImages.js +147 -0
- package/providers/maplibre/src/utils/symbolImages.test.js +248 -0
- package/src/App/components/Markers/Markers.jsx +122 -27
- package/src/App/components/Markers/Markers.module.scss +0 -10
- package/src/App/components/Markers/Markers.test.jsx +246 -0
- package/src/App/components/Panel/Panel.jsx +6 -6
- package/src/App/components/Panel/Panel.test.jsx +37 -0
- package/src/App/components/Viewport/Viewport.jsx +5 -15
- package/src/App/components/Viewport/Viewport.module.scss +2 -0
- package/src/App/components/Viewport/Viewport.test.jsx +16 -33
- package/src/App/hooks/useInterfaceAPI.js +7 -7
- package/src/App/hooks/useInterfaceAPI.test.js +162 -0
- package/src/App/hooks/useLayoutMeasurements.js +64 -72
- package/src/App/hooks/useMarkersAPI.js +2 -5
- package/src/App/hooks/useMarkersAPI.test.js +4 -4
- package/src/App/layout/Layout.jsx +3 -3
- package/src/App/layout/Layout.test.jsx +4 -2
- package/src/App/layout/layout.module.scss +1 -8
- package/src/App/renderer/HtmlElementHost.jsx +10 -5
- package/src/App/renderer/mapPanels.js +2 -1
- package/src/App/store/ServiceProvider.jsx +7 -5
- package/src/App/store/appActionsMap.js +4 -4
- package/src/App/store/appActionsMap.test.js +10 -0
- package/src/App/store/mapActionsMap.js +4 -6
- package/src/App/store/mapActionsMap.test.js +3 -2
- package/src/App/store/mapReducer.js +2 -1
- package/src/InteractiveMap/InteractiveMap.js +59 -11
- package/src/InteractiveMap/InteractiveMap.test.js +126 -4
- package/src/InteractiveMap/domStateManager.js +18 -6
- package/src/InteractiveMap/domStateManager.test.js +21 -0
- package/src/InteractiveMap/historyManager.js +28 -16
- package/src/InteractiveMap/historyManager.test.js +17 -0
- package/src/config/appConfig.js +2 -7
- package/src/config/appConfig.test.js +4 -15
- package/src/config/defaults.js +2 -3
- package/src/config/events.js +20 -21
- package/src/config/mapTheme.js +56 -0
- package/src/config/patternConfig.js +16 -0
- package/src/config/symbolConfig.js +80 -0
- package/src/scss/settings/_colors.scss +0 -9
- package/src/services/closeApp.js +1 -10
- package/src/services/closeApp.test.js +3 -43
- package/src/services/patternRegistry.js +40 -0
- package/src/services/patternRegistry.test.js +48 -0
- package/src/services/symbolRegistry.js +113 -0
- package/src/services/symbolRegistry.test.js +262 -0
- package/src/types.js +99 -12
- package/src/utils/mapStateSync.js +48 -10
- package/src/utils/mapStateSync.test.js +29 -9
- package/src/utils/patternUtils.js +94 -0
- package/src/utils/patternUtils.test.js +160 -0
- package/src/utils/symbolUtils.js +85 -0
- package/src/utils/symbolUtils.test.js +156 -0
- package/docs/examples.mdx +0 -70
- package/plugins/beta/datasets/src/adapters/maplibre/patternRegistry.js +0 -48
- package/plugins/beta/datasets/src/styles/patterns.js +0 -157
|
@@ -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.maplibreProvider=t())}(this,()=>(()=>{"use strict";var e,t,r,n,o={},i={};function a(e){var t=i[e];if(void 0!==t)return t.exports;var r=i[e]={exports:{}};return o[e].call(r.exports,r,r.exports,a),r.exports}a.m=o,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,a.t=function(r,n){if(1&n&&(r=this(r)),8&n)return r;if("object"==typeof r&&r){if(4&n&&r.__esModule)return r;if(16&n&&"function"==typeof r.then)return r}var o=Object.create(null);a.r(o);var i={};e=e||[null,t({}),t([]),t(t)];for(var u=2&n&&r;("object"==typeof u||"function"==typeof u)&&!~e.indexOf(u);u=t(u))Object.getOwnPropertyNames(u).forEach(e=>i[e]=()=>r[e]);return i.default=()=>r,a.d(o,i),o},a.d=(e,t)=>{for(var r in t)a.o(t,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce((t,r)=>(a.f[r](e,t),t),[])),a.u=e=>({649:"im-maplibre-framework",772:"im-maplibre-provider"}[e]+".js"),a.miniCssF=e=>{},a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},n="defra.maplibreProvider:",a.l=(e,t,o,i)=>{if(r[e])r[e].push(t);else{var u,c;if(void 0!==o)for(var f=document.getElementsByTagName("script"),l=0;l<f.length;l++){var p=f[l];if(p.getAttribute("src")==e||p.getAttribute("data-webpack")==n+o){u=p;break}}u||(c=!0,(u=document.createElement("script")).charset="utf-8",a.nc&&u.setAttribute("nonce",a.nc),u.setAttribute("data-webpack",n+o),u.src=e),r[e]=[t];var s=(t,n)=>{u.onerror=u.onload=null,clearTimeout(d);var o=r[e];if(delete r[e],u.parentNode&&u.parentNode.removeChild(u),o&&o.forEach(e=>e(n)),t)return t(n)},d=setTimeout(s.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=s.bind(null,u.onerror),u.onload=s.bind(null,u.onload),c&&document.head.appendChild(u)}},a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;a.g.importScripts&&(e=a.g.location+"");var t=a.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(/\/[^\/]+$/,"/"),a.p=e})(),(()=>{var e={57:0};a.f.j=(t,r)=>{var n=a.o(e,t)?e[t]:void 0;if(0!==n)if(n)r.push(n[2]);else{var o=new Promise((r,o)=>n=e[t]=[r,o]);r.push(n[2]=o);var i=a.p+a.u(t),u=new Error;a.l(i,r=>{if(a.o(e,t)&&(0!==(n=e[t])&&(e[t]=void 0),n)){var o=r&&("load"===r.type?"missing":r.type),i=r&&r.target&&r.target.src;u.message="Loading chunk "+t+" failed.\n("+o+": "+i+")",u.name="ChunkLoadError",u.type=o,u.request=i,n[1](u)}},"chunk-"+t,t)}};var t=(t,r)=>{var n,o,[i,u,c]=r,f=0;if(i.some(t=>0!==e[t])){for(n in u)a.o(u,n)&&(a.m[n]=u[n]);c&&c(a)}for(t&&t(r);f<i.length;f++)o=i[f],a.o(e,o)&&e[o]&&e[o][0](),e[o]=0},r=this.webpackChunkdefra_DefraMap=this.webpackChunkdefra_DefraMap||[];r.forEach(t.bind(null,0)),r.push=t.bind(null,r.push.bind(r))})();var u={};function c(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r<t;r++)n[r]=e[r];return n}a.d(u,{default:()=>m});var f=function(e){if(!window.WebGLRenderingContext)return{isEnabled:!1,error:"WebGL is not supported"};var t,r=document.createElement("canvas"),n=!1,o=function(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(e){if("string"==typeof e)return c(e,t);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?c(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,o=function(){};return{s:o,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,u=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return a=e.done,e},e:function(e){u=!0,i=e},f:function(){try{a||null==r.return||r.return()}finally{if(u)throw i}}}}(e);try{for(o.s();!(t=o.n()).done;){var i=t.value;try{if((n=r.getContext(i))&&"function"==typeof n.getParameter)return{isEnabled:!0}}catch(e){}}}catch(e){o.e(e)}finally{o.f()}return{isEnabled:!1,error:"WebGL is supported, but disabled"}};function l(e){return l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},l(e)}function p(){var e,t,r="function"==typeof Symbol?Symbol:{},n=r.iterator||"@@iterator",o=r.toStringTag||"@@toStringTag";function i(r,n,o,i){var c=n&&n.prototype instanceof u?n:u,f=Object.create(c.prototype);return s(f,"_invoke",function(r,n,o){var i,u,c,f=0,l=o||[],p=!1,s={p:0,n:0,v:e,a:d,f:d.bind(e,4),d:function(t,r){return i=t,u=0,c=e,s.n=r,a}};function d(r,n){for(u=r,c=n,t=0;!p&&f&&!o&&t<l.length;t++){var o,i=l[t],d=s.p,b=i[2];r>3?(o=b===n)&&(c=i[(u=i[4])?5:(u=3,3)],i[4]=i[5]=e):i[0]<=d&&((o=r<2&&d<i[1])?(u=0,s.v=n,s.n=i[1]):d<b&&(o=r<3||i[0]>n||n>b)&&(i[4]=r,i[5]=n,s.n=b,u=0))}if(o||r>1)return a;throw p=!0,n}return function(o,l,b){if(f>1)throw TypeError("Generator is already running");for(p&&1===l&&d(l,b),u=l,c=b;(t=u<2?e:c)||!p;){i||(u?u<3?(u>1&&(s.n=-1),d(u,c)):s.n=c:s.v=c);try{if(f=2,i){if(u||(o="next"),t=i[o]){if(!(t=t.call(i,c)))throw TypeError("iterator result is not an object");if(!t.done)return t;c=t.value,u<2&&(u=0)}else 1===u&&(t=i.return)&&t.call(i),u<2&&(c=TypeError("The iterator does not provide a '"+o+"' method"),u=1);i=e}else if((t=(p=s.n<0)?c:r.call(n,s))!==a)break}catch(t){i=e,u=1,c=t}finally{f=1}}return{value:t,done:p}}}(r,o,i),!0),f}var a={};function u(){}function c(){}function f(){}t=Object.getPrototypeOf;var l=[][n]?t(t([][n]())):(s(t={},n,function(){return this}),t),d=f.prototype=u.prototype=Object.create(l);function b(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,f):(e.__proto__=f,s(e,o,"GeneratorFunction")),e.prototype=Object.create(d),e}return c.prototype=f,s(d,"constructor",f),s(f,"constructor",c),c.displayName="GeneratorFunction",s(f,o,"GeneratorFunction"),s(d),s(d,o,"Generator"),s(d,n,function(){return this}),s(d,"toString",function(){return"[object Generator]"}),(p=function(){return{w:i,m:b}})()}function s(e,t,r,n){var o=Object.defineProperty;try{o({},"",{})}catch(e){o=0}s=function(e,t,r,n){function i(t,r){s(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))},s(e,t,r,n)}function d(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function b(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?d(Object(r),!0).forEach(function(t){y(e,t,r[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):d(Object(r)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))})}return e}function y(e,t,r){return(t=function(e){var t=function(e){if("object"!=l(e)||!e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var r=t.call(e,"string");if("object"!=l(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==l(t)?t:t+""}(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function v(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 m(){var e,t,r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{checkDeviceCapabilities:function(){var e=f(["webgl2","webgl1"]),t=document.documentMode;return{isSupported:e.isEnabled&&"function"==typeof"".replaceAll,error:t?"Internet Explorer is not supported":e.error}},load:(e=p().m(function e(){var t,n,o;return p().w(function(e){for(;;)switch(e.n){case 0:return e.n=1,a.e(649).then(a.t.bind(a,443,19));case 1:return t=e.v,e.n=2,a.e(772).then(a.bind(a,
|
|
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.maplibreProvider=t())}(this,()=>(()=>{"use strict";var e,t,r,n,o={738(e){e.exports=preactCompat},287(e){e.exports=preactJsxRuntime}},i={};function a(e){var t=i[e];if(void 0!==t)return t.exports;var r=i[e]={exports:{}};return o[e].call(r.exports,r,r.exports,a),r.exports}a.m=o,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,a.t=function(r,n){if(1&n&&(r=this(r)),8&n)return r;if("object"==typeof r&&r){if(4&n&&r.__esModule)return r;if(16&n&&"function"==typeof r.then)return r}var o=Object.create(null);a.r(o);var i={};e=e||[null,t({}),t([]),t(t)];for(var u=2&n&&r;("object"==typeof u||"function"==typeof u)&&!~e.indexOf(u);u=t(u))Object.getOwnPropertyNames(u).forEach(e=>i[e]=()=>r[e]);return i.default=()=>r,a.d(o,i),o},a.d=(e,t)=>{for(var r in t)a.o(t,r)&&!a.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},a.f={},a.e=e=>Promise.all(Object.keys(a.f).reduce((t,r)=>(a.f[r](e,t),t),[])),a.u=e=>({649:"im-maplibre-framework",772:"im-maplibre-provider"}[e]+".js"),a.miniCssF=e=>{},a.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),a.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r={},n="defra.maplibreProvider:",a.l=(e,t,o,i)=>{if(r[e])r[e].push(t);else{var u,c;if(void 0!==o)for(var f=document.getElementsByTagName("script"),l=0;l<f.length;l++){var p=f[l];if(p.getAttribute("src")==e||p.getAttribute("data-webpack")==n+o){u=p;break}}u||(c=!0,(u=document.createElement("script")).charset="utf-8",a.nc&&u.setAttribute("nonce",a.nc),u.setAttribute("data-webpack",n+o),u.src=e),r[e]=[t];var s=(t,n)=>{u.onerror=u.onload=null,clearTimeout(d);var o=r[e];if(delete r[e],u.parentNode&&u.parentNode.removeChild(u),o&&o.forEach(e=>e(n)),t)return t(n)},d=setTimeout(s.bind(null,void 0,{type:"timeout",target:u}),12e4);u.onerror=s.bind(null,u.onerror),u.onload=s.bind(null,u.onload),c&&document.head.appendChild(u)}},a.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;a.g.importScripts&&(e=a.g.location+"");var t=a.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(/\/[^\/]+$/,"/"),a.p=e})(),(()=>{var e={57:0};a.f.j=(t,r)=>{var n=a.o(e,t)?e[t]:void 0;if(0!==n)if(n)r.push(n[2]);else{var o=new Promise((r,o)=>n=e[t]=[r,o]);r.push(n[2]=o);var i=a.p+a.u(t),u=new Error;a.l(i,r=>{if(a.o(e,t)&&(0!==(n=e[t])&&(e[t]=void 0),n)){var o=r&&("load"===r.type?"missing":r.type),i=r&&r.target&&r.target.src;u.message="Loading chunk "+t+" failed.\n("+o+": "+i+")",u.name="ChunkLoadError",u.type=o,u.request=i,n[1](u)}},"chunk-"+t,t)}};var t=(t,r)=>{var n,o,[i,u,c]=r,f=0;if(i.some(t=>0!==e[t])){for(n in u)a.o(u,n)&&(a.m[n]=u[n]);c&&c(a)}for(t&&t(r);f<i.length;f++)o=i[f],a.o(e,o)&&e[o]&&e[o][0](),e[o]=0},r=this.webpackChunkdefra_DefraMap=this.webpackChunkdefra_DefraMap||[];r.forEach(t.bind(null,0)),r.push=t.bind(null,r.push.bind(r))})();var u={};function c(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r<t;r++)n[r]=e[r];return n}a.d(u,{default:()=>m});var f=function(e){if(!window.WebGLRenderingContext)return{isEnabled:!1,error:"WebGL is not supported"};var t,r=document.createElement("canvas"),n=!1,o=function(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(e){if("string"==typeof e)return c(e,t);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?c(e,t):void 0}}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,o=function(){};return{s:o,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,u=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return a=e.done,e},e:function(e){u=!0,i=e},f:function(){try{a||null==r.return||r.return()}finally{if(u)throw i}}}}(e);try{for(o.s();!(t=o.n()).done;){var i=t.value;try{if((n=r.getContext(i))&&"function"==typeof n.getParameter)return{isEnabled:!0}}catch(e){}}}catch(e){o.e(e)}finally{o.f()}return{isEnabled:!1,error:"WebGL is supported, but disabled"}};function l(e){return l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},l(e)}function p(){var e,t,r="function"==typeof Symbol?Symbol:{},n=r.iterator||"@@iterator",o=r.toStringTag||"@@toStringTag";function i(r,n,o,i){var c=n&&n.prototype instanceof u?n:u,f=Object.create(c.prototype);return s(f,"_invoke",function(r,n,o){var i,u,c,f=0,l=o||[],p=!1,s={p:0,n:0,v:e,a:d,f:d.bind(e,4),d:function(t,r){return i=t,u=0,c=e,s.n=r,a}};function d(r,n){for(u=r,c=n,t=0;!p&&f&&!o&&t<l.length;t++){var o,i=l[t],d=s.p,b=i[2];r>3?(o=b===n)&&(c=i[(u=i[4])?5:(u=3,3)],i[4]=i[5]=e):i[0]<=d&&((o=r<2&&d<i[1])?(u=0,s.v=n,s.n=i[1]):d<b&&(o=r<3||i[0]>n||n>b)&&(i[4]=r,i[5]=n,s.n=b,u=0))}if(o||r>1)return a;throw p=!0,n}return function(o,l,b){if(f>1)throw TypeError("Generator is already running");for(p&&1===l&&d(l,b),u=l,c=b;(t=u<2?e:c)||!p;){i||(u?u<3?(u>1&&(s.n=-1),d(u,c)):s.n=c:s.v=c);try{if(f=2,i){if(u||(o="next"),t=i[o]){if(!(t=t.call(i,c)))throw TypeError("iterator result is not an object");if(!t.done)return t;c=t.value,u<2&&(u=0)}else 1===u&&(t=i.return)&&t.call(i),u<2&&(c=TypeError("The iterator does not provide a '"+o+"' method"),u=1);i=e}else if((t=(p=s.n<0)?c:r.call(n,s))!==a)break}catch(t){i=e,u=1,c=t}finally{f=1}}return{value:t,done:p}}}(r,o,i),!0),f}var a={};function u(){}function c(){}function f(){}t=Object.getPrototypeOf;var l=[][n]?t(t([][n]())):(s(t={},n,function(){return this}),t),d=f.prototype=u.prototype=Object.create(l);function b(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,f):(e.__proto__=f,s(e,o,"GeneratorFunction")),e.prototype=Object.create(d),e}return c.prototype=f,s(d,"constructor",f),s(f,"constructor",c),c.displayName="GeneratorFunction",s(f,o,"GeneratorFunction"),s(d),s(d,o,"Generator"),s(d,n,function(){return this}),s(d,"toString",function(){return"[object Generator]"}),(p=function(){return{w:i,m:b}})()}function s(e,t,r,n){var o=Object.defineProperty;try{o({},"",{})}catch(e){o=0}s=function(e,t,r,n){function i(t,r){s(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))},s(e,t,r,n)}function d(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,n)}return r}function b(e){for(var t=1;t<arguments.length;t++){var r=null!=arguments[t]?arguments[t]:{};t%2?d(Object(r),!0).forEach(function(t){y(e,t,r[t])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(r)):d(Object(r)).forEach(function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(r,t))})}return e}function y(e,t,r){return(t=function(e){var t=function(e){if("object"!=l(e)||!e)return e;var t=e[Symbol.toPrimitive];if(void 0!==t){var r=t.call(e,"string");if("object"!=l(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(e)}(e);return"symbol"==l(t)?t:t+""}(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function v(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 m(){var e,t,r=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{checkDeviceCapabilities:function(){var e=f(["webgl2","webgl1"]),t=document.documentMode;return{isSupported:e.isEnabled&&"function"==typeof"".replaceAll,error:t?"Internet Explorer is not supported":e.error}},load:(e=p().m(function e(){var t,n,o;return p().w(function(e){for(;;)switch(e.n){case 0:return e.n=1,a.e(649).then(a.t.bind(a,443,19));case 1:return t=e.v,e.n=2,a.e(772).then(a.bind(a,335));case 2:return n=e.v.default,o=b(b({},r),{},{crs:"EPSG:4326"}),e.a(2,{MapProvider:n,mapProviderConfig:o,mapFramework:t})}},e)}),t=function(){var t=this,r=arguments;return new Promise(function(n,o){var i=e.apply(t,r);function a(e){v(i,n,o,a,u,"next",e)}function u(e){v(i,n,o,a,u,"throw",e)}a(void 0)})},function(){return t.apply(this,arguments)})}}return u.default})());
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export function attachAppEvents ({
|
|
2
|
+
mapProvider,
|
|
2
3
|
map,
|
|
3
4
|
events,
|
|
4
5
|
eventBus
|
|
@@ -16,13 +17,19 @@ export function attachAppEvents ({
|
|
|
16
17
|
map.setPixelRatio(pixelRatio)
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
const handleSizeChange = ({ mapSize }) => {
|
|
21
|
+
mapProvider.mapSize = mapSize
|
|
22
|
+
}
|
|
23
|
+
|
|
19
24
|
eventBus.on(events.MAP_SET_STYLE, handleSetMapStyle)
|
|
20
25
|
eventBus.on(events.MAP_SET_PIXEL_RATIO, handleSetPixelRatio)
|
|
26
|
+
eventBus.on(events.MAP_SIZE_CHANGE, handleSizeChange)
|
|
21
27
|
|
|
22
28
|
return {
|
|
23
29
|
remove () {
|
|
24
30
|
eventBus.off(events.MAP_SET_STYLE, handleSetMapStyle)
|
|
25
31
|
eventBus.off(events.MAP_SET_PIXEL_RATIO, handleSetPixelRatio)
|
|
32
|
+
eventBus.off(events.MAP_SIZE_CHANGE, handleSizeChange)
|
|
26
33
|
}
|
|
27
34
|
}
|
|
28
35
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { attachAppEvents } from './appEvents.js'
|
|
2
2
|
|
|
3
3
|
describe('attachAppEvents', () => {
|
|
4
|
-
let map, eventBus, events
|
|
4
|
+
let map, mapProvider, eventBus, events
|
|
5
5
|
|
|
6
6
|
beforeEach(() => {
|
|
7
7
|
map = {
|
|
@@ -9,6 +9,7 @@ describe('attachAppEvents', () => {
|
|
|
9
9
|
setPixelRatio: jest.fn(),
|
|
10
10
|
once: jest.fn()
|
|
11
11
|
}
|
|
12
|
+
mapProvider = { mapSize: null }
|
|
12
13
|
eventBus = {
|
|
13
14
|
on: jest.fn(),
|
|
14
15
|
off: jest.fn(),
|
|
@@ -17,16 +18,18 @@ describe('attachAppEvents', () => {
|
|
|
17
18
|
events = {
|
|
18
19
|
MAP_SET_STYLE: 'map:set-style',
|
|
19
20
|
MAP_SET_PIXEL_RATIO: 'map:set-pixel-ratio',
|
|
20
|
-
MAP_STYLE_CHANGE: 'map:stylechange'
|
|
21
|
+
MAP_STYLE_CHANGE: 'map:stylechange',
|
|
22
|
+
MAP_SIZE_CHANGE: 'map:size-change'
|
|
21
23
|
}
|
|
22
24
|
})
|
|
23
25
|
|
|
24
26
|
it('attaches handlers and triggers correct map methods', () => {
|
|
25
|
-
const controller = attachAppEvents({ map, events, eventBus })
|
|
27
|
+
const controller = attachAppEvents({ mapProvider, map, events, eventBus })
|
|
26
28
|
|
|
27
29
|
// Verify eventBus.on was called with correct handlers
|
|
28
30
|
expect(eventBus.on).toHaveBeenCalledWith(events.MAP_SET_STYLE, expect.any(Function))
|
|
29
31
|
expect(eventBus.on).toHaveBeenCalledWith(events.MAP_SET_PIXEL_RATIO, expect.any(Function))
|
|
32
|
+
expect(eventBus.on).toHaveBeenCalledWith(events.MAP_SIZE_CHANGE, expect.any(Function))
|
|
30
33
|
|
|
31
34
|
// Extract the attached handlers
|
|
32
35
|
const styleHandler = eventBus.on.mock.calls.find(c => c[0] === events.MAP_SET_STYLE)[1]
|
|
@@ -45,9 +48,20 @@ describe('attachAppEvents', () => {
|
|
|
45
48
|
styleLoadCallback()
|
|
46
49
|
expect(eventBus.emit).toHaveBeenCalledWith(events.MAP_STYLE_CHANGE, { mapStyleId: 'outdoor' })
|
|
47
50
|
|
|
48
|
-
// Verify remove detaches handlers
|
|
51
|
+
// Verify remove detaches all handlers
|
|
49
52
|
controller.remove()
|
|
50
53
|
expect(eventBus.off).toHaveBeenCalledWith(events.MAP_SET_STYLE, styleHandler)
|
|
51
54
|
expect(eventBus.off).toHaveBeenCalledWith(events.MAP_SET_PIXEL_RATIO, pixelHandler)
|
|
55
|
+
const sizeHandler = eventBus.on.mock.calls.find(c => c[0] === events.MAP_SIZE_CHANGE)[1]
|
|
56
|
+
expect(eventBus.off).toHaveBeenCalledWith(events.MAP_SIZE_CHANGE, sizeHandler)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('updates mapProvider.mapSize when MAP_SIZE_CHANGE fires', () => {
|
|
60
|
+
attachAppEvents({ mapProvider, map, events, eventBus })
|
|
61
|
+
|
|
62
|
+
const sizeHandler = eventBus.on.mock.calls.find(c => c[0] === events.MAP_SIZE_CHANGE)[1]
|
|
63
|
+
sizeHandler({ mapSize: 'large' })
|
|
64
|
+
|
|
65
|
+
expect(mapProvider.mapSize).toBe('large')
|
|
52
66
|
})
|
|
53
67
|
})
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { DEFAULTS, supportedShortcuts } from './defaults.js'
|
|
7
|
+
import { scaleFactor } from '../../../src/config/appConfig.js'
|
|
7
8
|
import { cleanCanvas, applyPreventDefaultFix } from './utils/maplibreFixes.js'
|
|
8
9
|
import { attachMapEvents } from './mapEvents.js'
|
|
9
10
|
import { attachAppEvents } from './appEvents.js'
|
|
@@ -11,6 +12,9 @@ import { getAreaDimensions, getCardinalMove, getBboxFromGeoJSON, isGeometryObscu
|
|
|
11
12
|
import { createMapLabelNavigator } from './utils/labels.js'
|
|
12
13
|
import { updateHighlightedFeatures } from './utils/highlightFeatures.js'
|
|
13
14
|
import { queryFeatures } from './utils/queryFeatures.js'
|
|
15
|
+
import { setupHoverCursor } from './utils/hoverCursor.js'
|
|
16
|
+
import { registerSymbols } from './utils/symbolImages.js'
|
|
17
|
+
import { registerPatterns } from './utils/patternImages.js'
|
|
14
18
|
|
|
15
19
|
/**
|
|
16
20
|
* MapLibre GL JS implementation of the MapProvider interface.
|
|
@@ -113,6 +117,7 @@ export default class MapLibreProvider {
|
|
|
113
117
|
|
|
114
118
|
/** Destroy the map and clean up resources. */
|
|
115
119
|
destroyMap () {
|
|
120
|
+
this.setHoverCursor([])
|
|
116
121
|
this.mapEvents?.remove()
|
|
117
122
|
this.appEvents?.remove()
|
|
118
123
|
|
|
@@ -122,6 +127,19 @@ export default class MapLibreProvider {
|
|
|
122
127
|
this.map.remove()
|
|
123
128
|
}
|
|
124
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Set pointer cursor on the map canvas when hovering over any of the given layer IDs.
|
|
132
|
+
* Call with an empty array to remove all hover cursor listeners.
|
|
133
|
+
*
|
|
134
|
+
* @param {string[]} layerIds
|
|
135
|
+
*/
|
|
136
|
+
setHoverCursor (layerIds) {
|
|
137
|
+
if (!this.map) {
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
this._onHoverMove = setupHoverCursor(this.map, layerIds, this._onHoverMove)
|
|
141
|
+
}
|
|
142
|
+
|
|
125
143
|
// ==========================
|
|
126
144
|
// Side-effects
|
|
127
145
|
// ==========================
|
|
@@ -283,6 +301,40 @@ export default class MapLibreProvider {
|
|
|
283
301
|
return queryFeatures(this.map, point, options)
|
|
284
302
|
}
|
|
285
303
|
|
|
304
|
+
/**
|
|
305
|
+
* Rasterise and register symbol images for the given pre-resolved symbol configs.
|
|
306
|
+
* Delegates to the shared symbol image utility so any plugin's MapLibre adapter can
|
|
307
|
+
* register symbols without importing provider internals directly.
|
|
308
|
+
*
|
|
309
|
+
* The pixel ratio is computed as device pixel ratio × map size scale factor so symbols
|
|
310
|
+
* are rasterised at the correct resolution for the current device DPI and map size.
|
|
311
|
+
*
|
|
312
|
+
* @param {Object[]} symbolConfigs - Flat list of datasets/merged-sublayers with a symbol config.
|
|
313
|
+
* Callers are responsible for sublayer merging before passing configs here.
|
|
314
|
+
* @param {Object} mapStyle - Current map style config (provides id, selectedColor, haloColor)
|
|
315
|
+
* @param {Object} symbolRegistry
|
|
316
|
+
* @returns {Promise<void>}
|
|
317
|
+
*/
|
|
318
|
+
async registerSymbols (symbolConfigs, mapStyle, symbolRegistry) {
|
|
319
|
+
const pixelRatio = (this.map.getPixelRatio() || 1) * (scaleFactor[this.mapSize] || 1)
|
|
320
|
+
return registerSymbols(this.map, symbolConfigs, mapStyle, symbolRegistry, pixelRatio)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Rasterise and register pattern images for the given pre-resolved pattern configs.
|
|
325
|
+
* Delegates to the shared pattern image utility so any plugin's MapLibre adapter can
|
|
326
|
+
* register patterns without importing provider internals directly.
|
|
327
|
+
*
|
|
328
|
+
* @param {Object[]} patternConfigs - Flat list of datasets/merged-sublayers with a pattern config.
|
|
329
|
+
* Callers are responsible for sublayer merging before passing configs here.
|
|
330
|
+
* @param {string} mapStyleId
|
|
331
|
+
* @param {Object} patternRegistry
|
|
332
|
+
* @returns {Promise<void>}
|
|
333
|
+
*/
|
|
334
|
+
async registerPatterns (patternConfigs, mapStyleId, patternRegistry) {
|
|
335
|
+
return registerPatterns(this.map, patternConfigs, mapStyleId, patternRegistry)
|
|
336
|
+
}
|
|
337
|
+
|
|
286
338
|
// ==========================
|
|
287
339
|
// Spatial helpers
|
|
288
340
|
// ==========================
|
|
@@ -4,6 +4,8 @@ import { attachAppEvents } from './appEvents.js'
|
|
|
4
4
|
import { createMapLabelNavigator } from './utils/labels.js'
|
|
5
5
|
import { updateHighlightedFeatures } from './utils/highlightFeatures.js'
|
|
6
6
|
import { queryFeatures } from './utils/queryFeatures.js'
|
|
7
|
+
import { registerSymbols } from './utils/symbolImages.js'
|
|
8
|
+
import { registerPatterns } from './utils/patternImages.js'
|
|
7
9
|
import { getAreaDimensions, getCardinalMove, getResolution, getPaddedBounds, isGeometryObscured } from './utils/spatial.js'
|
|
8
10
|
|
|
9
11
|
jest.mock('./defaults.js', () => ({
|
|
@@ -33,6 +35,8 @@ jest.mock('./utils/labels.js', () => ({
|
|
|
33
35
|
}))
|
|
34
36
|
jest.mock('./utils/highlightFeatures.js', () => ({ updateHighlightedFeatures: jest.fn(() => []) }))
|
|
35
37
|
jest.mock('./utils/queryFeatures.js', () => ({ queryFeatures: jest.fn(() => []) }))
|
|
38
|
+
jest.mock('./utils/symbolImages.js', () => ({ registerSymbols: jest.fn(() => Promise.resolve()) }))
|
|
39
|
+
jest.mock('./utils/patternImages.js', () => ({ registerPatterns: jest.fn(() => Promise.resolve()) }))
|
|
36
40
|
|
|
37
41
|
describe('MapLibreProvider', () => {
|
|
38
42
|
let map, eventBus, maplibreModule, loadCallback
|
|
@@ -53,7 +57,11 @@ describe('MapLibreProvider', () => {
|
|
|
53
57
|
unproject: jest.fn(() => ({ lng: 1, lat: 2 })),
|
|
54
58
|
getCenter: jest.fn(() => ({ lng: 1.2345678, lat: 2.3456789 })),
|
|
55
59
|
getZoom: jest.fn(() => 10),
|
|
56
|
-
getBounds: jest.fn(() => ({ toArray: jest.fn(() => [[0, 0], [1, 1]]) }))
|
|
60
|
+
getBounds: jest.fn(() => ({ toArray: jest.fn(() => [[0, 0], [1, 1]]) })),
|
|
61
|
+
getPixelRatio: jest.fn(() => 1),
|
|
62
|
+
getCanvas: jest.fn(() => ({ style: {} })),
|
|
63
|
+
getLayer: jest.fn(() => true),
|
|
64
|
+
queryRenderedFeatures: jest.fn(() => [])
|
|
57
65
|
}
|
|
58
66
|
eventBus = { emit: jest.fn() }
|
|
59
67
|
maplibreModule = { Map: jest.fn(() => map), LngLatBounds: jest.fn() }
|
|
@@ -220,6 +228,102 @@ describe('MapLibreProvider', () => {
|
|
|
220
228
|
})
|
|
221
229
|
})
|
|
222
230
|
|
|
231
|
+
test('registerSymbols delegates to utility with map instance', async () => {
|
|
232
|
+
const p = makeProvider()
|
|
233
|
+
await doInitMap(p)
|
|
234
|
+
const configs = [{ symbol: 'pin' }]
|
|
235
|
+
const mapStyle = { id: 'test', selectedColor: '#0b0c0c' }
|
|
236
|
+
const registry = {}
|
|
237
|
+
await p.registerSymbols(configs, mapStyle, registry)
|
|
238
|
+
expect(registerSymbols).toHaveBeenCalledWith(map, configs, mapStyle, registry, expect.any(Number))
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
test('registerSymbols computes pixelRatio from getPixelRatio and mapSize scale factor', async () => {
|
|
242
|
+
const p = makeProvider()
|
|
243
|
+
await doInitMap(p)
|
|
244
|
+
map.getPixelRatio.mockReturnValue(2)
|
|
245
|
+
p.mapSize = 'medium' // scaleFactor['medium'] = 1.5
|
|
246
|
+
const registry = {}
|
|
247
|
+
await p.registerSymbols([], { id: 'test' }, registry)
|
|
248
|
+
expect(registerSymbols).toHaveBeenCalledWith(map, [], { id: 'test' }, registry, 3) // 2 * 1.5
|
|
249
|
+
})
|
|
250
|
+
|
|
251
|
+
test('registerSymbols falls back to pixelRatio 1 when getPixelRatio returns 0', async () => {
|
|
252
|
+
const p = makeProvider()
|
|
253
|
+
await doInitMap(p)
|
|
254
|
+
map.getPixelRatio.mockReturnValue(0)
|
|
255
|
+
p.mapSize = 'small' // scaleFactor['small'] = 1
|
|
256
|
+
const registry = {}
|
|
257
|
+
await p.registerSymbols([], { id: 'test' }, registry)
|
|
258
|
+
expect(registerSymbols).toHaveBeenCalledWith(map, [], { id: 'test' }, registry, 1) // (0 || 1) * 1
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
test('registerPatterns delegates to utility with map instance', async () => {
|
|
262
|
+
const p = makeProvider()
|
|
263
|
+
await doInitMap(p)
|
|
264
|
+
const configs = [{ fillPattern: 'dot' }]
|
|
265
|
+
const registry = {}
|
|
266
|
+
await p.registerPatterns(configs, 'test', registry)
|
|
267
|
+
expect(registerPatterns).toHaveBeenCalledWith(map, configs, 'test', registry)
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
describe('setHoverCursor', () => {
|
|
271
|
+
test('registers mousemove handler on the map when layerIds provided', async () => {
|
|
272
|
+
const p = makeProvider()
|
|
273
|
+
await doInitMap(p)
|
|
274
|
+
p.setHoverCursor(['layer-a'])
|
|
275
|
+
expect(map.on).toHaveBeenCalledWith('mousemove', expect.any(Function))
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
test('sets cursor to pointer when queryRenderedFeatures returns a hit', async () => {
|
|
279
|
+
const canvas = { style: {} }
|
|
280
|
+
map.getCanvas.mockReturnValue(canvas)
|
|
281
|
+
map.queryRenderedFeatures.mockReturnValue([{ id: 'f1' }])
|
|
282
|
+
|
|
283
|
+
const p = makeProvider()
|
|
284
|
+
await doInitMap(p)
|
|
285
|
+
p.setHoverCursor(['layer-a'])
|
|
286
|
+
|
|
287
|
+
const moveHandler = map.on.mock.calls.find(([e]) => e === 'mousemove')?.[1]
|
|
288
|
+
moveHandler({ point: { x: 10, y: 20 } })
|
|
289
|
+
|
|
290
|
+
expect(canvas.style.cursor).toBe('pointer')
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
test('clears cursor when queryRenderedFeatures returns no hit', async () => {
|
|
294
|
+
const canvas = { style: { cursor: 'pointer' } }
|
|
295
|
+
map.getCanvas.mockReturnValue(canvas)
|
|
296
|
+
map.queryRenderedFeatures.mockReturnValue([])
|
|
297
|
+
|
|
298
|
+
const p = makeProvider()
|
|
299
|
+
await doInitMap(p)
|
|
300
|
+
p.setHoverCursor(['layer-a'])
|
|
301
|
+
|
|
302
|
+
const moveHandler = map.on.mock.calls.find(([e]) => e === 'mousemove')?.[1]
|
|
303
|
+
moveHandler({ point: { x: 10, y: 20 } })
|
|
304
|
+
|
|
305
|
+
expect(canvas.style.cursor).toBe('')
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
test('removes mousemove handler and clears cursor when called with empty array', async () => {
|
|
309
|
+
const canvas = { style: { cursor: 'pointer' } }
|
|
310
|
+
map.getCanvas.mockReturnValue(canvas)
|
|
311
|
+
|
|
312
|
+
const p = makeProvider()
|
|
313
|
+
await doInitMap(p)
|
|
314
|
+
p.setHoverCursor(['layer-a'])
|
|
315
|
+
p.setHoverCursor([])
|
|
316
|
+
|
|
317
|
+
expect(map.off).toHaveBeenCalledWith('mousemove', expect.any(Function))
|
|
318
|
+
expect(canvas.style.cursor).toBe('')
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
test('does nothing when map is not initialised', () => {
|
|
322
|
+
const p = makeProvider()
|
|
323
|
+
expect(() => p.setHoverCursor(['layer-a'])).not.toThrow()
|
|
324
|
+
})
|
|
325
|
+
})
|
|
326
|
+
|
|
223
327
|
test('label methods return null without labelNavigator; delegate when set', async () => {
|
|
224
328
|
const p = makeProvider()
|
|
225
329
|
await doInitMap(p)
|
|
@@ -36,7 +36,7 @@ const cleanupStaleSources = (map, previousSources, currentSources) => {
|
|
|
36
36
|
previousSources.forEach(src => {
|
|
37
37
|
if (!currentSources.has(src)) {
|
|
38
38
|
const base = `highlight-${src}`
|
|
39
|
-
const layers = [`${base}-fill`, `${base}-line`]
|
|
39
|
+
const layers = [`${base}-fill`, `${base}-line`, `${base}-symbol`]
|
|
40
40
|
layers.forEach(id => {
|
|
41
41
|
if (map.getLayer(id)) {
|
|
42
42
|
map.setFilter(id, ['==', 'id', ''])
|
|
@@ -63,6 +63,25 @@ const applyHighlightLayer = (map, id, type, sourceId, srcLayer, paint, filter) =
|
|
|
63
63
|
map.moveLayer(id)
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
const applySymbolHighlightLayer = (map, id, sourceId, srcLayer, originalLayerId, imageId, filter) => {
|
|
67
|
+
if (!map.getLayer(id)) {
|
|
68
|
+
map.addLayer({
|
|
69
|
+
id,
|
|
70
|
+
type: 'symbol',
|
|
71
|
+
source: sourceId,
|
|
72
|
+
...(srcLayer && { 'source-layer': srcLayer }),
|
|
73
|
+
layout: {
|
|
74
|
+
'icon-image': imageId,
|
|
75
|
+
'icon-anchor': map.getLayoutProperty(originalLayerId, 'icon-anchor') ?? 'center',
|
|
76
|
+
'icon-allow-overlap': true
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
map.setLayoutProperty(id, 'icon-image', imageId)
|
|
81
|
+
map.setFilter(id, filter)
|
|
82
|
+
map.moveLayer(id)
|
|
83
|
+
}
|
|
84
|
+
|
|
66
85
|
const calculateBounds = (LngLatBounds, renderedFeatures) => {
|
|
67
86
|
if (!renderedFeatures.length) {
|
|
68
87
|
return null
|
|
@@ -78,9 +97,11 @@ const calculateBounds = (LngLatBounds, renderedFeatures) => {
|
|
|
78
97
|
return [bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth()]
|
|
79
98
|
}
|
|
80
99
|
|
|
100
|
+
const getSelectedImageId = (map, imageId) => map._symbolImageMap?.[imageId] ?? null
|
|
101
|
+
|
|
81
102
|
/**
|
|
82
103
|
* Update highlighted features using pure filters.
|
|
83
|
-
* Supports fill
|
|
104
|
+
* Supports fill, line and symbol geometry, multi-source, cleanup, and bounds.
|
|
84
105
|
*/
|
|
85
106
|
export function updateHighlightedFeatures ({ LngLatBounds, map, selectedFeatures, stylesMap }) {
|
|
86
107
|
if (!map) {
|
|
@@ -106,21 +127,21 @@ export function updateHighlightedFeatures ({ LngLatBounds, map, selectedFeatures
|
|
|
106
127
|
const geom = hasFillGeometry ? 'fill' : baseLayer.type
|
|
107
128
|
const base = `highlight-${sourceId}`
|
|
108
129
|
|
|
109
|
-
const { stroke, strokeWidth, fill } = stylesMap[layerId]
|
|
110
|
-
// Use ['id'] for feature.id, ['get', idProperty] for properties
|
|
111
130
|
const idExpression = idProperty ? ['get', idProperty] : ['id']
|
|
112
131
|
const filter = ['in', idExpression, ['literal', [...ids]]]
|
|
113
|
-
const fillFilter = ['in', idExpression, ['literal', [...fillIds]]]
|
|
114
|
-
|
|
115
|
-
const linePaint = { 'line-color': stroke, 'line-width': strokeWidth }
|
|
116
132
|
|
|
117
133
|
if (geom === 'fill') {
|
|
134
|
+
const { stroke, strokeWidth, fill } = stylesMap[layerId]
|
|
135
|
+
const fillFilter = ['in', idExpression, ['literal', [...fillIds]]]
|
|
136
|
+
const linePaint = { 'line-color': stroke, 'line-width': strokeWidth }
|
|
118
137
|
// Only apply fill highlight to polygon features, not to any co-selected line features
|
|
119
138
|
applyHighlightLayer(map, `${base}-fill`, 'fill', sourceId, srcLayer, { 'fill-color': fill }, fillFilter)
|
|
120
139
|
applyHighlightLayer(map, `${base}-line`, 'line', sourceId, srcLayer, linePaint, filter)
|
|
121
140
|
}
|
|
122
141
|
|
|
123
142
|
if (geom === 'line') {
|
|
143
|
+
const { stroke, strokeWidth } = stylesMap[layerId]
|
|
144
|
+
const linePaint = { 'line-color': stroke, 'line-width': strokeWidth }
|
|
124
145
|
// Clear any fill highlight from a previous polygon selection on the same source
|
|
125
146
|
if (map.getLayer(`${base}-fill`)) {
|
|
126
147
|
map.setFilter(`${base}-fill`, ['==', 'id', ''])
|
|
@@ -128,6 +149,14 @@ export function updateHighlightedFeatures ({ LngLatBounds, map, selectedFeatures
|
|
|
128
149
|
applyHighlightLayer(map, `${base}-line`, 'line', sourceId, srcLayer, linePaint, filter)
|
|
129
150
|
}
|
|
130
151
|
|
|
152
|
+
if (geom === 'symbol') {
|
|
153
|
+
const imageId = map.getLayoutProperty(layerId, 'icon-image')
|
|
154
|
+
const selectedImageId = getSelectedImageId(map, imageId)
|
|
155
|
+
if (selectedImageId) {
|
|
156
|
+
applySymbolHighlightLayer(map, `${base}-symbol`, sourceId, srcLayer, layerId, selectedImageId, filter)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
131
160
|
// Bounds only from rendered tiles
|
|
132
161
|
renderedFeatures.push(
|
|
133
162
|
...map
|