react-ol-choropleth
Version:
A React plugin for creating choropleth maps using OpenLayers
1 lines • 7.67 kB
JavaScript
var e,r;e=this,r=function(e,r,t,o,n,c,l,s,a,i,u,m,d,g,h,f){const p=({data:e,valueProperty:r,colorScale:o})=>t.useMemo((()=>{if(!o||!e.length)return()=>"#cccccc";const t=e.map((e=>Number(e.get(r)))).filter((e=>!isNaN(e))).sort(((e,r)=>e-r));if(!t.length)return()=>"#cccccc";const n=t[0],c=t[t.length-1],{type:l,colors:s}=o,a=(e=>{try{const r=e.map((e=>f(e).hex()));return f.scale(r)}catch(r){return f.scale(["#f7fbff","#4292c6"])}})(s);switch(l){case"sequential":{const e=s.length,r=Array.from({length:e+1},((r,o)=>{const n=Math.floor(o*(t.length-1)/e);return t[n]}));return t=>{if(isNaN(t))return"#cccccc";const o=r.findIndex(((e,o)=>t>=r[o]&&(o===r.length-1||t<r[o+1])));if(-1===o)return s[0];if(o===r.length-1)return s[s.length-1];const n=r[o],c=r[o+1];return a(o/(e-1)+(t-n)/(c-n)/e).hex()}}case"diverging":{const e=t[Math.floor(t.length/2)];return r=>{if(isNaN(r))return"#cccccc";let t;return t=r<=e?(r-n)/(e-n)*.5:.5+(r-e)/(c-e)*.5,a(t).hex()}}case"categorical":{const e=Array.from(new Set(t));return r=>{if(isNaN(r))return"#cccccc";const t=e.indexOf(r);return-1===t?"#cccccc":s[t%s.length]}}default:return()=>"#cccccc"}}),[e,r,o]),y=({colorScale:e,position:t,values:o,className:n=""})=>{const{type:c,colors:l}=e,[s,a]=[Math.min(...o),Math.max(...o)],i=(()=>{if("categorical"===c)return Array.from(new Set(o)).sort().map(String);if("diverging"===c){const e=(a+s)/2;return[s.toFixed(1),e.toFixed(1),a.toFixed(1)]}return l.map(((e,r)=>(s+r*(a-s)/(l.length-1)).toFixed(1)))})(),u="categorical"===c?l.slice(0,i.length):l;return r.jsx("div",{className:`react-ol-choropleth__legend ${n}`.trim(),"data-position":t,children:u.map(((e,t)=>r.jsxs("div",{className:"react-ol-choropleth__legend-item",children:[r.jsx("div",{className:"react-ol-choropleth__legend-color",style:{backgroundColor:e}}),r.jsx("span",{className:"react-ol-choropleth__legend-label",children:i[t]})]},t)))})},v=(e,r=300)=>{let t;const o=function(...o){clearTimeout(t),t=setTimeout((()=>e.apply(this,o)),r)};return o.cancel=()=>clearTimeout(t),o},x=t.memo((({data:e,valueProperty:x,colorScale:j,style:N,zoom:S=2,baseMap:P="osm",showLegend:w=!0,legendPosition:M="top-right",onFeatureClick:b,onFeatureHover:F,overlayOptions:O={positioning:"bottom-center",offset:[0,-10],autoPan:!0,trigger:"click"},zoomToFeature:_=!1,selectedFeatureBorderColor:q="#0099ff",canZoomOutBoundaries:C=!0,className:R="",mapClassName:E="",legendClassName:k=""})=>{const A=t.useRef(null),G=t.useRef(null),T=t.useRef(null),V=t.useRef(null),Z=t.useRef(null),[z,I]=t.useState([]),B=t.useRef(null),J=t.useRef(null),L=t.useRef(null),[W,X]=t.useState(null),Y=t.useMemo((()=>{const r=new a;if(Array.isArray(e))r.addFeatures(e);else{const t=(new m).readFeatures(e,{featureProjection:"EPSG:3857",dataProjection:"EPSG:4326"});r.addFeatures(t)}const t=r.getFeatures();return T.current=r,I(t),r}),[e]),$=p({data:z,valueProperty:x,colorScale:j}),D=t.useCallback((e=>{const r=e.get(x),t=$(Number(r)),o="#cccccc"===t?[204,204,204]:f(t).rgb(),n=e===B.current,c=e===J.current,l=f(q).rgb();return new d.Style({fill:new d.Fill({color:[...o,.8]}),stroke:new d.Stroke({color:n||c?[...l,1]:[61,61,61,1],width:n?3:c?2:1})})}),[$,x,q]),H=t.useCallback(((e,t)=>{if(e&&V.current&&Z.current&&O)try{const o=O.render?O.render(e):(e=>{const t=e.getProperties(),o=Object.entries(t).filter((([e])=>"geometry"!==e)).map((([e,t])=>r.jsxs("div",{className:"react-ol-choropleth__overlay-property",children:[r.jsxs("strong",{children:[e,":"]})," ",String(t)]},e)));return r.jsx("div",{className:"react-ol-choropleth__overlay",children:o})})(e);X(o);const n=e.getGeometry();if(n instanceof g){const e=n.getExtent(),r=t||[(e[0]+e[2])/2,e[3]];requestAnimationFrame((()=>{V.current&&V.current.setPosition(r)}))}}catch(o){V.current.setPosition(void 0),X(null)}else V.current&&(V.current.setPosition(void 0),X(null))}),[O]),K=t.useCallback(((e,r)=>{const t=r.forEachFeatureAtPixel(e.pixel,(e=>e))||null;if(B.current=t,O&&"click"===O.trigger&&H(t),t){if(b){const o=r.getCoordinateFromPixel(e.pixel);b(t,o)}if(_&&G.current){const e=t.getGeometry();if(e instanceof g){const r=e.getExtent(),t=G.current.getView(),o=t.getResolutionForExtent(r,G.current.getSize()||void 0),n=t.getZoomForResolution(o||1),c=[(r[0]+r[2])/2,r[3]];t.animate({center:c,zoom:n?Math.min(n+.5,6):6,duration:500})}}}else b&&b(null);L.current&&L.current.changed()}),[b,_,O,H]),Q=t.useCallback(((e,r)=>{const t="pointermove"===e.type&&r.forEachFeatureAtPixel(e.pixel,(e=>e))||null;if(J.current=t,O&&"hover"===O.trigger){const o=t?r.getCoordinateFromPixel(e.pixel):void 0;H(t,o)}F&&F(t),L.current&&L.current.changed()}),[F,O,H]);t.useEffect((()=>{if(!A.current||!Y)return;const e=new s({source:Y,style:N||D,updateWhileAnimating:!0,updateWhileInteracting:!0});L.current=e;const r=[];"none"!==P&&("satellite"===P?r.push(new l({source:new u({url:"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",maxZoom:19})})):r.push(new l({source:new i})));const t=[...r,e],o=Y.getExtent(),a=A.current.getBoundingClientRect(),m=Math.max((o[2]-o[0])/a.width,(o[3]-o[1])/a.height),d=Math.floor(Math.log2(156543.03392804097)-Math.log2(m)),f=new c({projection:"EPSG:3857",...C?{}:{extent:o,minZoom:Math.max(d-1,0),constrainOnlyCenter:!1}}),p=new n({target:A.current,layers:t,view:f});f.fit(o,{padding:[50,50,50,50],maxZoom:S||void 0,duration:0}),G.current=p;let y=null;if(O)try{const e=document.createElement("div");e.className="react-ol-choropleth__overlay-container",Z.current=e;const r=new h({element:e,positioning:O.positioning||"bottom-center",offset:O.offset||[0,-10],stopEvent:!1,className:"react-ol-choropleth__overlay-wrapper",autoPan:!1!==O.autoPan});V.current=r,p.addOverlay(r),y=v((()=>{if(B.current&&V.current){const e=B.current.getGeometry();if(e instanceof g){const r=e.getExtent(),t=[(r[0]+r[2])/2,r[3]];V.current.setPosition(t)}}}),150),p.on("moveend",y)}catch(w){}const x=e=>K(e,p);p.on("click",x);const j=e=>Q(e,p);return p.on("pointermove",j),()=>{y&&(y.cancel(),p.un("moveend",y)),p.un("click",x),p.un("pointermove",j),V.current&&(p.removeOverlay(V.current),V.current=null),Z.current&&(Z.current.remove(),Z.current=null),p.dispose()}}),[Y,N,D,P,O,K,Q,S,C]);const U=t.useMemo((()=>z.map((e=>Number(e.get(x)))).filter((e=>!isNaN(e)))),[z,x]);return r.jsxs("div",{className:`react-ol-choropleth ${R}`.trim(),children:[r.jsx("div",{ref:A,className:`react-ol-choropleth__map ${E}`.trim()}),w&&j&&U.length>0&&r.jsx(y,{colorScale:j,position:M,values:U,className:k}),W&&Z.current&&o.createPortal(W,Z.current)]})}));e.ChoroplethMap=x,e.Legend=y,e.useColorScale=p,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})},"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("react/jsx-runtime"),require("react"),require("react-dom"),require("ol/Map.js"),require("ol/View.js"),require("ol/layer/Tile.js"),require("ol/layer/Vector.js"),require("ol/source/Vector.js"),require("ol/source/OSM.js"),require("ol/source/XYZ.js"),require("ol/format/GeoJSON.js"),require("ol/style.js"),require("ol/geom/Polygon.js"),require("ol/Overlay.js"),require("chroma-js")):"function"==typeof define&&define.amd?define(["exports","react/jsx-runtime","react","react-dom","ol/Map.js","ol/View.js","ol/layer/Tile.js","ol/layer/Vector.js","ol/source/Vector.js","ol/source/OSM.js","ol/source/XYZ.js","ol/format/GeoJSON.js","ol/style.js","ol/geom/Polygon.js","ol/Overlay.js","chroma-js"],r):r((e="undefined"!=typeof globalThis?globalThis:e||self).ReactOLChoropleth={},e.jsxRuntime,e.React,e.ReactDOM,e.ol.Map,e.ol.View,e.ol.layer.Tile,e.ol.layer.Vector,e.ol.source.Vector,e.ol.source.OSM,e.ol.source.XYZ,e.ol.format.GeoJSON,e.ol.style,e.ol.geom.Polygon,e.ol.Overlay,e.chroma);