@geowiki/map 0.16.8-dev.1 → 0.16.9-dev.1

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/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import * as react from 'react';
3
- import react__default, { CSSProperties, ForwardRefExoticComponent, RefAttributes } from 'react';
2
+ import * as React$1 from 'react';
3
+ import React__default, { CSSProperties, ForwardRefExoticComponent, RefAttributes } from 'react';
4
4
  import * as L from 'leaflet';
5
5
  import L__default, { LatLngBoundsExpression, WMSOptions, WMSParams, Map, LatLng } from 'leaflet';
6
6
  import { TypedArray } from 'geotiff';
@@ -23,7 +23,7 @@ declare const POSITION_CLASSES: {
23
23
  middleRight: string;
24
24
  };
25
25
  interface GenericControlProps {
26
- children?: react__default.ReactNode;
26
+ children?: React__default.ReactNode;
27
27
  position?: keyof typeof POSITION_CLASSES;
28
28
  cssclass?: string;
29
29
  cssStyle?: CSSProperties;
@@ -590,7 +590,7 @@ declare const BasicMap: () => react_jsx_runtime.JSX.Element;
590
590
  interface BasfMapProps extends Partial<MapContainerProps> {
591
591
  className?: string;
592
592
  }
593
- declare const BasfMap: react.NamedExoticComponent<BasfMapProps>;
593
+ declare const BasfMap: React$1.NamedExoticComponent<BasfMapProps>;
594
594
 
595
595
  declare const GeoTreesMap: () => react_jsx_runtime.JSX.Element;
596
596
 
@@ -601,7 +601,7 @@ interface Props {
601
601
  mapSettings: MapSettingsItemDto;
602
602
  session: any;
603
603
  }
604
- declare const GeoWikiMap: react__default.MemoExoticComponent<({ mapSettings, session }: Props) => react_jsx_runtime.JSX.Element>;
604
+ declare const GeoWikiMap: React__default.MemoExoticComponent<({ mapSettings, session }: Props) => react_jsx_runtime.JSX.Element>;
605
605
 
606
606
  type BaseLayerType = "Esri World imagery" | "OpenStreetMap" | "Google Hybrid" | "Google Satellite";
607
607
 
@@ -613,6 +613,42 @@ declare const WaybackTimelineMap: (props: {
613
613
  mapZoom: number;
614
614
  }) => react_jsx_runtime.JSX.Element;
615
615
 
616
+ interface MapLibreProps extends React__default.HTMLAttributes<HTMLDivElement> {
617
+ baseLayers?: {
618
+ name: string;
619
+ type: string;
620
+ layer_type?: "hillshade" | "terrainSource";
621
+ url?: string;
622
+ tileSize?: number;
623
+ }[];
624
+ customLayers?: {
625
+ id: string;
626
+ index: number;
627
+ isCustom: boolean;
628
+ type: "GeoJson";
629
+ url: string;
630
+ layerName: string;
631
+ color: string;
632
+ opacity: number;
633
+ title: string;
634
+ }[];
635
+ mapControls?: {
636
+ type: "zoom" | "projection" | "toggle_layers";
637
+ }[];
638
+ sourceData?: {
639
+ name: string;
640
+ type: "geojson";
641
+ data: any;
642
+ is_cluster?: boolean;
643
+ show_cluster_count?: boolean;
644
+ style?: {
645
+ cluster_color: string;
646
+ cluster_stroke_color: string;
647
+ };
648
+ }[];
649
+ }
650
+ declare const MapLibre: React__default.ForwardRefExoticComponent<MapLibreProps & React__default.RefAttributes<HTMLDivElement>>;
651
+
616
652
  interface BingLayerOptions extends L.TileLayerOptions {
617
653
  culture?: string | undefined;
618
654
  style?: string;
@@ -637,4 +673,4 @@ declare class BingLayer extends L.TileLayer {
637
673
 
638
674
  declare const BingLeafletLayer: ForwardRefExoticComponent<BingLayerOptions & RefAttributes<BingLayer>>;
639
675
 
640
- export { Annotation, type AnnotationUndoRedo, type Asset, BasfMap, BasicMap, BingLeafletLayer, CDSE_Layer, CanopyMap, CanvasImage, CustomWMSTileLayer, type CustomWMSTileLayerProps, EvoLandGeometry, type EvoLandRasterType, EvolandContainer, EvolandLeftSideBar, GenericControl, GeoJsonLayer, GeoTreesMap, GeoWikiMap, type ICanvasImageProps, type IMapSidebarProps, type IReadOnlyTiffImageProps, type ITiffImageProps, type ITiffWithD3Props, LabelButton, type LabelButtonProps, LabelButtonView, type LabelButtonViewProps, LayerStoreList, type LocationWithGeometry, MapEvents, MapSidebar, OverlayLayer, PointSelection, Questions, type RasterAssetItem, type RasterItem, type RasterItemBase64, RasterType, ReadOnlyTiffImage, SegmentationType, SelectedPoint, TiffImage, TiffImageContainer, TimeSeries, ViewAssets, WaybackTimelineMap, useEvolandStore, useMapLayerStore, useMapStore };
676
+ export { Annotation, type AnnotationUndoRedo, type Asset, BasfMap, BasicMap, BingLeafletLayer, CDSE_Layer, CanopyMap, CanvasImage, CustomWMSTileLayer, type CustomWMSTileLayerProps, EvoLandGeometry, type EvoLandRasterType, EvolandContainer, EvolandLeftSideBar, GenericControl, GeoJsonLayer, GeoTreesMap, GeoWikiMap, type ICanvasImageProps, type IMapSidebarProps, type IReadOnlyTiffImageProps, type ITiffImageProps, type ITiffWithD3Props, LabelButton, type LabelButtonProps, LabelButtonView, type LabelButtonViewProps, LayerStoreList, type LocationWithGeometry, MapEvents, MapLibre, MapSidebar, OverlayLayer, PointSelection, Questions, type RasterAssetItem, type RasterItem, type RasterItemBase64, RasterType, ReadOnlyTiffImage, SegmentationType, SelectedPoint, TiffImage, TiffImageContainer, TimeSeries, ViewAssets, WaybackTimelineMap, useEvolandStore, useMapLayerStore, useMapStore };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import * as react from 'react';
3
- import react__default, { CSSProperties, ForwardRefExoticComponent, RefAttributes } from 'react';
2
+ import * as React$1 from 'react';
3
+ import React__default, { CSSProperties, ForwardRefExoticComponent, RefAttributes } from 'react';
4
4
  import * as L from 'leaflet';
5
5
  import L__default, { LatLngBoundsExpression, WMSOptions, WMSParams, Map, LatLng } from 'leaflet';
6
6
  import { TypedArray } from 'geotiff';
@@ -23,7 +23,7 @@ declare const POSITION_CLASSES: {
23
23
  middleRight: string;
24
24
  };
25
25
  interface GenericControlProps {
26
- children?: react__default.ReactNode;
26
+ children?: React__default.ReactNode;
27
27
  position?: keyof typeof POSITION_CLASSES;
28
28
  cssclass?: string;
29
29
  cssStyle?: CSSProperties;
@@ -590,7 +590,7 @@ declare const BasicMap: () => react_jsx_runtime.JSX.Element;
590
590
  interface BasfMapProps extends Partial<MapContainerProps> {
591
591
  className?: string;
592
592
  }
593
- declare const BasfMap: react.NamedExoticComponent<BasfMapProps>;
593
+ declare const BasfMap: React$1.NamedExoticComponent<BasfMapProps>;
594
594
 
595
595
  declare const GeoTreesMap: () => react_jsx_runtime.JSX.Element;
596
596
 
@@ -601,7 +601,7 @@ interface Props {
601
601
  mapSettings: MapSettingsItemDto;
602
602
  session: any;
603
603
  }
604
- declare const GeoWikiMap: react__default.MemoExoticComponent<({ mapSettings, session }: Props) => react_jsx_runtime.JSX.Element>;
604
+ declare const GeoWikiMap: React__default.MemoExoticComponent<({ mapSettings, session }: Props) => react_jsx_runtime.JSX.Element>;
605
605
 
606
606
  type BaseLayerType = "Esri World imagery" | "OpenStreetMap" | "Google Hybrid" | "Google Satellite";
607
607
 
@@ -613,6 +613,42 @@ declare const WaybackTimelineMap: (props: {
613
613
  mapZoom: number;
614
614
  }) => react_jsx_runtime.JSX.Element;
615
615
 
616
+ interface MapLibreProps extends React__default.HTMLAttributes<HTMLDivElement> {
617
+ baseLayers?: {
618
+ name: string;
619
+ type: string;
620
+ layer_type?: "hillshade" | "terrainSource";
621
+ url?: string;
622
+ tileSize?: number;
623
+ }[];
624
+ customLayers?: {
625
+ id: string;
626
+ index: number;
627
+ isCustom: boolean;
628
+ type: "GeoJson";
629
+ url: string;
630
+ layerName: string;
631
+ color: string;
632
+ opacity: number;
633
+ title: string;
634
+ }[];
635
+ mapControls?: {
636
+ type: "zoom" | "projection" | "toggle_layers";
637
+ }[];
638
+ sourceData?: {
639
+ name: string;
640
+ type: "geojson";
641
+ data: any;
642
+ is_cluster?: boolean;
643
+ show_cluster_count?: boolean;
644
+ style?: {
645
+ cluster_color: string;
646
+ cluster_stroke_color: string;
647
+ };
648
+ }[];
649
+ }
650
+ declare const MapLibre: React__default.ForwardRefExoticComponent<MapLibreProps & React__default.RefAttributes<HTMLDivElement>>;
651
+
616
652
  interface BingLayerOptions extends L.TileLayerOptions {
617
653
  culture?: string | undefined;
618
654
  style?: string;
@@ -637,4 +673,4 @@ declare class BingLayer extends L.TileLayer {
637
673
 
638
674
  declare const BingLeafletLayer: ForwardRefExoticComponent<BingLayerOptions & RefAttributes<BingLayer>>;
639
675
 
640
- export { Annotation, type AnnotationUndoRedo, type Asset, BasfMap, BasicMap, BingLeafletLayer, CDSE_Layer, CanopyMap, CanvasImage, CustomWMSTileLayer, type CustomWMSTileLayerProps, EvoLandGeometry, type EvoLandRasterType, EvolandContainer, EvolandLeftSideBar, GenericControl, GeoJsonLayer, GeoTreesMap, GeoWikiMap, type ICanvasImageProps, type IMapSidebarProps, type IReadOnlyTiffImageProps, type ITiffImageProps, type ITiffWithD3Props, LabelButton, type LabelButtonProps, LabelButtonView, type LabelButtonViewProps, LayerStoreList, type LocationWithGeometry, MapEvents, MapSidebar, OverlayLayer, PointSelection, Questions, type RasterAssetItem, type RasterItem, type RasterItemBase64, RasterType, ReadOnlyTiffImage, SegmentationType, SelectedPoint, TiffImage, TiffImageContainer, TimeSeries, ViewAssets, WaybackTimelineMap, useEvolandStore, useMapLayerStore, useMapStore };
676
+ export { Annotation, type AnnotationUndoRedo, type Asset, BasfMap, BasicMap, BingLeafletLayer, CDSE_Layer, CanopyMap, CanvasImage, CustomWMSTileLayer, type CustomWMSTileLayerProps, EvoLandGeometry, type EvoLandRasterType, EvolandContainer, EvolandLeftSideBar, GenericControl, GeoJsonLayer, GeoTreesMap, GeoWikiMap, type ICanvasImageProps, type IMapSidebarProps, type IReadOnlyTiffImageProps, type ITiffImageProps, type ITiffWithD3Props, LabelButton, type LabelButtonProps, LabelButtonView, type LabelButtonViewProps, LayerStoreList, type LocationWithGeometry, MapEvents, MapLibre, MapSidebar, OverlayLayer, PointSelection, Questions, type RasterAssetItem, type RasterItem, type RasterItemBase64, RasterType, ReadOnlyTiffImage, SegmentationType, SelectedPoint, TiffImage, TiffImageContainer, TimeSeries, ViewAssets, WaybackTimelineMap, useEvolandStore, useMapLayerStore, useMapStore };
package/dist/index.js CHANGED
@@ -470,6 +470,7 @@ __export(src_exports, {
470
470
  LabelButtonView: () => LabelButtonView,
471
471
  LayerStoreList: () => LayerStoreList,
472
472
  MapEvents: () => MapEvents,
473
+ MapLibre: () => MapLibre,
473
474
  MapSidebar: () => MapSidebar,
474
475
  OverlayLayer: () => OverlayLayer,
475
476
  PointSelection: () => PointSelection,
@@ -16673,6 +16674,276 @@ var WaybackTimelineMap = (props) => {
16673
16674
  }
16674
16675
  ) });
16675
16676
  };
16677
+
16678
+ // src/Components/Map/MapLibre.tsx
16679
+ var import_react62 = require("react");
16680
+ var import_react63 = require("react");
16681
+ var import_maplibre_gl = __toESM(require("maplibre-gl"));
16682
+ var import_MapPinIcon4 = __toESM(require("@heroicons/react/24/solid/MapPinIcon"));
16683
+ var import_server8 = require("react-dom/server");
16684
+ var import_design_system = require("@geowiki/design-system");
16685
+ var import_jsx_runtime79 = require("react/jsx-runtime");
16686
+ var MapLibre = (0, import_react63.forwardRef)(({ baseLayers, customLayers, mapControls, sourceData }, ref) => {
16687
+ const mapContainer = ref;
16688
+ const mapRef = (0, import_react62.useRef)(null);
16689
+ const changeMapProjection = () => {
16690
+ var _a;
16691
+ if (!mapRef.current)
16692
+ return;
16693
+ const map = mapRef.current;
16694
+ const currentProjection = (_a = map.getProjection()) == null ? void 0 : _a.type;
16695
+ const newProjection = currentProjection === "mercator" ? "globe" : "mercator";
16696
+ map.setProjection({
16697
+ type: newProjection
16698
+ });
16699
+ };
16700
+ const zoomIn = () => {
16701
+ if (!mapRef.current)
16702
+ return;
16703
+ const map = mapRef.current;
16704
+ map.zoomIn();
16705
+ };
16706
+ const zoomOut = () => {
16707
+ if (!mapRef.current)
16708
+ return;
16709
+ const map = mapRef.current;
16710
+ map.zoomOut();
16711
+ };
16712
+ function getLayersByPattern(map, pattern) {
16713
+ return map.getStyle().layers.filter((layer) => pattern.test(layer.id)).map((layer) => layer.id);
16714
+ }
16715
+ const handleToggle = (layerId) => {
16716
+ if (!mapRef.current)
16717
+ return;
16718
+ const map = mapRef.current;
16719
+ const layers = getLayersByPattern(map, new RegExp(layerId));
16720
+ layers.forEach((id) => {
16721
+ const visibility = map.getLayoutProperty(id, "visibility");
16722
+ const newVisibility = visibility === "none" ? "visible" : "none";
16723
+ map.setLayoutProperty(id, "visibility", newVisibility);
16724
+ });
16725
+ };
16726
+ const buildControls = (controls) => {
16727
+ return controls.map((c) => {
16728
+ switch (c.type) {
16729
+ case "projection":
16730
+ return {
16731
+ id: "projection",
16732
+ title: "Change Projection",
16733
+ icon: import_design_system.GlobeIcon,
16734
+ onClick: changeMapProjection,
16735
+ position: "topright",
16736
+ isOpen: true,
16737
+ isDisplayOnly: false,
16738
+ popoverDirection: "right",
16739
+ isMobileFilter: true
16740
+ };
16741
+ case "zoom":
16742
+ return [
16743
+ {
16744
+ id: "zoomIn",
16745
+ icon: import_design_system.PlusIcon,
16746
+ onClick: zoomIn,
16747
+ position: "topright",
16748
+ isOpen: true,
16749
+ isDisplayOnly: false,
16750
+ popoverDirection: "right",
16751
+ isMobileFilter: true
16752
+ },
16753
+ {
16754
+ id: "zoomOut",
16755
+ icon: import_design_system.MinusIcon,
16756
+ onClick: zoomOut,
16757
+ position: "topright",
16758
+ isOpen: true,
16759
+ isDisplayOnly: false,
16760
+ popoverDirection: "right",
16761
+ isMobileFilter: true
16762
+ }
16763
+ ];
16764
+ case "toggle_layers":
16765
+ let custom_layers = customLayers == null ? void 0 : customLayers.map((l) => __spreadProps(__spreadValues({}, l), {
16766
+ ontoggle: () => handleToggle(l.id)
16767
+ }));
16768
+ return {
16769
+ id: "overlay",
16770
+ icon: import_design_system.LayerGroupIcon,
16771
+ panel: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
16772
+ import_design_system.LayerSwitcher,
16773
+ {
16774
+ mapMenuItems: [
16775
+ {
16776
+ MapMenuItem: {},
16777
+ Layers: [],
16778
+ Title: ""
16779
+ }
16780
+ ],
16781
+ customLayers: custom_layers
16782
+ }
16783
+ ),
16784
+ position: "topleft",
16785
+ isOpen: false,
16786
+ isDisplayOnly: false,
16787
+ popoverDirection: "right",
16788
+ isMobileFilter: true
16789
+ };
16790
+ default:
16791
+ return null;
16792
+ }
16793
+ }).flat();
16794
+ };
16795
+ const [mapControl, setMapControl] = (0, import_react62.useState)(() => buildControls(mapControls));
16796
+ const toggleControl = (id) => {
16797
+ setMapControl((prevControls) => prevControls.map((control) => {
16798
+ if (control.id === id) {
16799
+ return __spreadProps(__spreadValues({}, control), { isOpen: !control.isOpen });
16800
+ }
16801
+ return control;
16802
+ }));
16803
+ };
16804
+ (0, import_react62.useEffect)(() => {
16805
+ if (typeof mapContainer === "function")
16806
+ return;
16807
+ if (!(mapContainer == null ? void 0 : mapContainer.current))
16808
+ return;
16809
+ if (!sourceData)
16810
+ return;
16811
+ const map = new import_maplibre_gl.default.Map({
16812
+ container: mapContainer == null ? void 0 : mapContainer.current,
16813
+ style: {
16814
+ version: 8,
16815
+ sources: {
16816
+ osm: {
16817
+ type: "raster",
16818
+ tiles: ["https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"],
16819
+ tileSize: 256,
16820
+ attribution: "&copy; OpenStreetMap Contributors",
16821
+ maxzoom: 19
16822
+ }
16823
+ },
16824
+ layers: [
16825
+ {
16826
+ id: "osm",
16827
+ type: "raster",
16828
+ source: "osm"
16829
+ }
16830
+ ],
16831
+ sky: {}
16832
+ },
16833
+ center: [16.3713, 48.2081],
16834
+ zoom: 3,
16835
+ minZoom: 1.5,
16836
+ renderWorldCopies: false,
16837
+ canvasContextAttributes: { antialias: true }
16838
+ });
16839
+ mapRef.current = map;
16840
+ map.scrollZoom.disable();
16841
+ mapRef.current.getContainer().addEventListener("mouseenter", () => map.scrollZoom.enable());
16842
+ mapRef.current.getContainer().addEventListener("mouseleave", () => map.scrollZoom.disable());
16843
+ function loadSvgImage() {
16844
+ return __async(this, null, function* () {
16845
+ let svgText = (0, import_server8.renderToStaticMarkup)(
16846
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_MapPinIcon4.default, { className: "w-6 h-6" })
16847
+ );
16848
+ svgText = svgText.replace('fill="currentColor"', 'fill="#06b6d4"');
16849
+ const img = new Image();
16850
+ img.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svgText);
16851
+ img.width = 35;
16852
+ img.height = 35;
16853
+ yield img.decode();
16854
+ return img;
16855
+ });
16856
+ }
16857
+ map.on("load", () => __async(void 0, null, function* () {
16858
+ changeMapProjection();
16859
+ baseLayers == null ? void 0 : baseLayers.forEach((layer) => {
16860
+ map.addSource(layer.name, {
16861
+ type: layer.type,
16862
+ url: layer.url,
16863
+ tileSize: layer.tileSize
16864
+ });
16865
+ if (layer.layer_type == "terrainSource")
16866
+ map.setTerrain({
16867
+ source: "terrainSource",
16868
+ exaggeration: 1
16869
+ });
16870
+ if (layer.layer_type == "hillshade")
16871
+ map.addLayer({
16872
+ id: layer.name,
16873
+ type: layer.layer_type,
16874
+ source: layer.name,
16875
+ paint: { "hillshade-shadow-color": "#473B24" },
16876
+ layout: { visibility: "visible" }
16877
+ });
16878
+ });
16879
+ sourceData == null ? void 0 : sourceData.forEach((source) => __async(void 0, null, function* () {
16880
+ if (source.is_cluster) {
16881
+ map.addSource(source.name, {
16882
+ type: source.type,
16883
+ data: source.data,
16884
+ cluster: true,
16885
+ clusterMaxZoom: 10,
16886
+ clusterRadius: 50
16887
+ });
16888
+ }
16889
+ if (source.show_cluster_count) {
16890
+ map.addLayer({
16891
+ id: `${source.name}-cluster-circles`,
16892
+ type: "circle",
16893
+ source: source.name,
16894
+ filter: ["has", "point_count"],
16895
+ paint: {
16896
+ "circle-color": source.style.cluster_color,
16897
+ "circle-opacity": 1,
16898
+ "circle-radius": 20,
16899
+ "circle-stroke-width": 4,
16900
+ "circle-stroke-color": source.style.cluster_stroke_color
16901
+ }
16902
+ });
16903
+ map.addLayer({
16904
+ id: `${source.name}-cluster-count`,
16905
+ type: "symbol",
16906
+ source: source.name,
16907
+ filter: ["has", "point_count"],
16908
+ layout: {
16909
+ "text-field": "{point_count_abbreviated}",
16910
+ "text-font": ["Open Sans Bold"],
16911
+ "text-size": 14
16912
+ },
16913
+ paint: {
16914
+ "text-color": "#f7f2f2"
16915
+ }
16916
+ });
16917
+ if (!map.hasImage("pin")) {
16918
+ const img = yield loadSvgImage();
16919
+ map.addImage("pin", img);
16920
+ }
16921
+ map.addLayer({
16922
+ id: source.name + "-count",
16923
+ type: "symbol",
16924
+ source: source.name,
16925
+ filter: ["!", ["has", "point_count"]],
16926
+ layout: {
16927
+ "icon-image": "pin",
16928
+ "icon-size": 1,
16929
+ "icon-allow-overlap": true,
16930
+ "icon-anchor": "bottom"
16931
+ }
16932
+ });
16933
+ }
16934
+ }));
16935
+ }));
16936
+ return () => {
16937
+ map.remove();
16938
+ };
16939
+ }, [sourceData, mapControls]);
16940
+ if (!sourceData)
16941
+ return;
16942
+ return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "relative", style: { width: "100%", height: "100vh" }, children: [
16943
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("div", { ref: mapContainer, className: "w-full h-full" }),
16944
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(import_design_system.MapControlToolbar, { controls: mapControl, onToggleControl: (id) => toggleControl(id) })
16945
+ ] });
16946
+ });
16676
16947
  // Annotate the CommonJS export names for ESM import in node:
16677
16948
  0 && (module.exports = {
16678
16949
  Annotation,
@@ -16694,6 +16965,7 @@ var WaybackTimelineMap = (props) => {
16694
16965
  LabelButtonView,
16695
16966
  LayerStoreList,
16696
16967
  MapEvents,
16968
+ MapLibre,
16697
16969
  MapSidebar,
16698
16970
  OverlayLayer,
16699
16971
  PointSelection,
package/dist/index.mjs CHANGED
@@ -16867,6 +16867,276 @@ var WaybackTimelineMap = (props) => {
16867
16867
  }
16868
16868
  ) });
16869
16869
  };
16870
+
16871
+ // src/Components/Map/MapLibre.tsx
16872
+ import { useEffect as useEffect34, useRef as useRef16, useState as useState36 } from "react";
16873
+ import { forwardRef as forwardRef5 } from "react";
16874
+ import maplibregl from "maplibre-gl";
16875
+ import MapPinIcon5 from "@heroicons/react/24/solid/MapPinIcon";
16876
+ import { renderToStaticMarkup } from "react-dom/server";
16877
+ import { MapControlToolbar, GlobeIcon as GlobeIcon2, PlusIcon as PlusIcon3, MinusIcon as MinusIcon3, LayerGroupIcon as LayerGroupIcon5, LayerSwitcher as LayerSwitcher2 } from "@geowiki/design-system";
16878
+ import { jsx as jsx79, jsxs as jsxs57 } from "react/jsx-runtime";
16879
+ var MapLibre = forwardRef5(({ baseLayers, customLayers, mapControls, sourceData }, ref) => {
16880
+ const mapContainer = ref;
16881
+ const mapRef = useRef16(null);
16882
+ const changeMapProjection = () => {
16883
+ var _a;
16884
+ if (!mapRef.current)
16885
+ return;
16886
+ const map = mapRef.current;
16887
+ const currentProjection = (_a = map.getProjection()) == null ? void 0 : _a.type;
16888
+ const newProjection = currentProjection === "mercator" ? "globe" : "mercator";
16889
+ map.setProjection({
16890
+ type: newProjection
16891
+ });
16892
+ };
16893
+ const zoomIn = () => {
16894
+ if (!mapRef.current)
16895
+ return;
16896
+ const map = mapRef.current;
16897
+ map.zoomIn();
16898
+ };
16899
+ const zoomOut = () => {
16900
+ if (!mapRef.current)
16901
+ return;
16902
+ const map = mapRef.current;
16903
+ map.zoomOut();
16904
+ };
16905
+ function getLayersByPattern(map, pattern) {
16906
+ return map.getStyle().layers.filter((layer) => pattern.test(layer.id)).map((layer) => layer.id);
16907
+ }
16908
+ const handleToggle = (layerId) => {
16909
+ if (!mapRef.current)
16910
+ return;
16911
+ const map = mapRef.current;
16912
+ const layers = getLayersByPattern(map, new RegExp(layerId));
16913
+ layers.forEach((id) => {
16914
+ const visibility = map.getLayoutProperty(id, "visibility");
16915
+ const newVisibility = visibility === "none" ? "visible" : "none";
16916
+ map.setLayoutProperty(id, "visibility", newVisibility);
16917
+ });
16918
+ };
16919
+ const buildControls = (controls) => {
16920
+ return controls.map((c) => {
16921
+ switch (c.type) {
16922
+ case "projection":
16923
+ return {
16924
+ id: "projection",
16925
+ title: "Change Projection",
16926
+ icon: GlobeIcon2,
16927
+ onClick: changeMapProjection,
16928
+ position: "topright",
16929
+ isOpen: true,
16930
+ isDisplayOnly: false,
16931
+ popoverDirection: "right",
16932
+ isMobileFilter: true
16933
+ };
16934
+ case "zoom":
16935
+ return [
16936
+ {
16937
+ id: "zoomIn",
16938
+ icon: PlusIcon3,
16939
+ onClick: zoomIn,
16940
+ position: "topright",
16941
+ isOpen: true,
16942
+ isDisplayOnly: false,
16943
+ popoverDirection: "right",
16944
+ isMobileFilter: true
16945
+ },
16946
+ {
16947
+ id: "zoomOut",
16948
+ icon: MinusIcon3,
16949
+ onClick: zoomOut,
16950
+ position: "topright",
16951
+ isOpen: true,
16952
+ isDisplayOnly: false,
16953
+ popoverDirection: "right",
16954
+ isMobileFilter: true
16955
+ }
16956
+ ];
16957
+ case "toggle_layers":
16958
+ let custom_layers = customLayers == null ? void 0 : customLayers.map((l) => __spreadProps(__spreadValues({}, l), {
16959
+ ontoggle: () => handleToggle(l.id)
16960
+ }));
16961
+ return {
16962
+ id: "overlay",
16963
+ icon: LayerGroupIcon5,
16964
+ panel: /* @__PURE__ */ jsx79(
16965
+ LayerSwitcher2,
16966
+ {
16967
+ mapMenuItems: [
16968
+ {
16969
+ MapMenuItem: {},
16970
+ Layers: [],
16971
+ Title: ""
16972
+ }
16973
+ ],
16974
+ customLayers: custom_layers
16975
+ }
16976
+ ),
16977
+ position: "topleft",
16978
+ isOpen: false,
16979
+ isDisplayOnly: false,
16980
+ popoverDirection: "right",
16981
+ isMobileFilter: true
16982
+ };
16983
+ default:
16984
+ return null;
16985
+ }
16986
+ }).flat();
16987
+ };
16988
+ const [mapControl, setMapControl] = useState36(() => buildControls(mapControls));
16989
+ const toggleControl = (id) => {
16990
+ setMapControl((prevControls) => prevControls.map((control) => {
16991
+ if (control.id === id) {
16992
+ return __spreadProps(__spreadValues({}, control), { isOpen: !control.isOpen });
16993
+ }
16994
+ return control;
16995
+ }));
16996
+ };
16997
+ useEffect34(() => {
16998
+ if (typeof mapContainer === "function")
16999
+ return;
17000
+ if (!(mapContainer == null ? void 0 : mapContainer.current))
17001
+ return;
17002
+ if (!sourceData)
17003
+ return;
17004
+ const map = new maplibregl.Map({
17005
+ container: mapContainer == null ? void 0 : mapContainer.current,
17006
+ style: {
17007
+ version: 8,
17008
+ sources: {
17009
+ osm: {
17010
+ type: "raster",
17011
+ tiles: ["https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"],
17012
+ tileSize: 256,
17013
+ attribution: "&copy; OpenStreetMap Contributors",
17014
+ maxzoom: 19
17015
+ }
17016
+ },
17017
+ layers: [
17018
+ {
17019
+ id: "osm",
17020
+ type: "raster",
17021
+ source: "osm"
17022
+ }
17023
+ ],
17024
+ sky: {}
17025
+ },
17026
+ center: [16.3713, 48.2081],
17027
+ zoom: 3,
17028
+ minZoom: 1.5,
17029
+ renderWorldCopies: false,
17030
+ canvasContextAttributes: { antialias: true }
17031
+ });
17032
+ mapRef.current = map;
17033
+ map.scrollZoom.disable();
17034
+ mapRef.current.getContainer().addEventListener("mouseenter", () => map.scrollZoom.enable());
17035
+ mapRef.current.getContainer().addEventListener("mouseleave", () => map.scrollZoom.disable());
17036
+ function loadSvgImage() {
17037
+ return __async(this, null, function* () {
17038
+ let svgText = renderToStaticMarkup(
17039
+ /* @__PURE__ */ jsx79(MapPinIcon5, { className: "w-6 h-6" })
17040
+ );
17041
+ svgText = svgText.replace('fill="currentColor"', 'fill="#06b6d4"');
17042
+ const img = new Image();
17043
+ img.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svgText);
17044
+ img.width = 35;
17045
+ img.height = 35;
17046
+ yield img.decode();
17047
+ return img;
17048
+ });
17049
+ }
17050
+ map.on("load", () => __async(void 0, null, function* () {
17051
+ changeMapProjection();
17052
+ baseLayers == null ? void 0 : baseLayers.forEach((layer) => {
17053
+ map.addSource(layer.name, {
17054
+ type: layer.type,
17055
+ url: layer.url,
17056
+ tileSize: layer.tileSize
17057
+ });
17058
+ if (layer.layer_type == "terrainSource")
17059
+ map.setTerrain({
17060
+ source: "terrainSource",
17061
+ exaggeration: 1
17062
+ });
17063
+ if (layer.layer_type == "hillshade")
17064
+ map.addLayer({
17065
+ id: layer.name,
17066
+ type: layer.layer_type,
17067
+ source: layer.name,
17068
+ paint: { "hillshade-shadow-color": "#473B24" },
17069
+ layout: { visibility: "visible" }
17070
+ });
17071
+ });
17072
+ sourceData == null ? void 0 : sourceData.forEach((source) => __async(void 0, null, function* () {
17073
+ if (source.is_cluster) {
17074
+ map.addSource(source.name, {
17075
+ type: source.type,
17076
+ data: source.data,
17077
+ cluster: true,
17078
+ clusterMaxZoom: 10,
17079
+ clusterRadius: 50
17080
+ });
17081
+ }
17082
+ if (source.show_cluster_count) {
17083
+ map.addLayer({
17084
+ id: `${source.name}-cluster-circles`,
17085
+ type: "circle",
17086
+ source: source.name,
17087
+ filter: ["has", "point_count"],
17088
+ paint: {
17089
+ "circle-color": source.style.cluster_color,
17090
+ "circle-opacity": 1,
17091
+ "circle-radius": 20,
17092
+ "circle-stroke-width": 4,
17093
+ "circle-stroke-color": source.style.cluster_stroke_color
17094
+ }
17095
+ });
17096
+ map.addLayer({
17097
+ id: `${source.name}-cluster-count`,
17098
+ type: "symbol",
17099
+ source: source.name,
17100
+ filter: ["has", "point_count"],
17101
+ layout: {
17102
+ "text-field": "{point_count_abbreviated}",
17103
+ "text-font": ["Open Sans Bold"],
17104
+ "text-size": 14
17105
+ },
17106
+ paint: {
17107
+ "text-color": "#f7f2f2"
17108
+ }
17109
+ });
17110
+ if (!map.hasImage("pin")) {
17111
+ const img = yield loadSvgImage();
17112
+ map.addImage("pin", img);
17113
+ }
17114
+ map.addLayer({
17115
+ id: source.name + "-count",
17116
+ type: "symbol",
17117
+ source: source.name,
17118
+ filter: ["!", ["has", "point_count"]],
17119
+ layout: {
17120
+ "icon-image": "pin",
17121
+ "icon-size": 1,
17122
+ "icon-allow-overlap": true,
17123
+ "icon-anchor": "bottom"
17124
+ }
17125
+ });
17126
+ }
17127
+ }));
17128
+ }));
17129
+ return () => {
17130
+ map.remove();
17131
+ };
17132
+ }, [sourceData, mapControls]);
17133
+ if (!sourceData)
17134
+ return;
17135
+ return /* @__PURE__ */ jsxs57("div", { className: "relative", style: { width: "100%", height: "100vh" }, children: [
17136
+ /* @__PURE__ */ jsx79("div", { ref: mapContainer, className: "w-full h-full" }),
17137
+ /* @__PURE__ */ jsx79(MapControlToolbar, { controls: mapControl, onToggleControl: (id) => toggleControl(id) })
17138
+ ] });
17139
+ });
16870
17140
  export {
16871
17141
  Annotation,
16872
17142
  BasfMap,
@@ -16887,6 +17157,7 @@ export {
16887
17157
  LabelButtonView,
16888
17158
  LayerStoreList,
16889
17159
  MapEvents,
17160
+ MapLibre,
16890
17161
  MapSidebar,
16891
17162
  OverlayLayer,
16892
17163
  PointSelection,
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@geowiki/map",
3
- "version": "0.16.8-dev.1",
3
+ "version": "0.16.9-dev.1",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [
7
7
  "dist",
8
+ "src/types",
8
9
  "README.md"
9
10
  ],
10
11
  "license": "MIT",
@@ -23,17 +24,18 @@
23
24
  "react-hook-form": "^7.53.0",
24
25
  "react-hotkeys-hook": "^4.5.0",
25
26
  "react-leaflet": "^4.2.1",
26
- "zod": "^3.23.8",
27
- "@geowiki/core": "0.16.8-dev.1",
28
- "@geowiki/ui": "0.16.8-dev.1",
29
- "@geowiki/evoland-api-proxy": "0.16.8-dev.1",
30
- "@geowiki/cms-proxy": "0.16.8-dev.1"
27
+ "zod": "^3.25.7",
28
+ "@geowiki/cms-proxy": "0.16.9-dev.1",
29
+ "@geowiki/core": "0.16.9-dev.1",
30
+ "@geowiki/design-system": "0.16.9-dev.1",
31
+ "@geowiki/evoland-api-proxy": "0.16.9-dev.1",
32
+ "@geowiki/ui": "0.16.9-dev.1"
31
33
  },
32
34
  "dependencies": {
33
35
  "@dnd-kit/core": "^6.3.1",
34
36
  "@dnd-kit/sortable": "^10.0.0",
35
37
  "@dnd-kit/utilities": "^3.2.2",
36
- "@headlessui/react": "^1.7.16",
38
+ "@headlessui/react": "^2.2.10",
37
39
  "@heroicons/react": "^2.1.1",
38
40
  "@hookform/resolvers": "^3.3.1",
39
41
  "@radix-ui/react-popover": "^1.0.6",
@@ -53,6 +55,7 @@
53
55
  "leaflet.markercluster": "^1.5.3",
54
56
  "leaflet.polylinemeasure": "^3.0.0",
55
57
  "lucide-react": "^0.224.0",
58
+ "maplibre-gl": "^5.23.0",
56
59
  "plotty": "^0.4.9",
57
60
  "pngjs": "^7.0.0",
58
61
  "proj4": "^2.9.0",
@@ -63,11 +66,14 @@
63
66
  "zustand": "4.3.9"
64
67
  },
65
68
  "devDependencies": {
69
+ "@tailwindcss/aspect-ratio": "^0.4.2",
70
+ "@tailwindcss/forms": "^0.5.3",
71
+ "@tailwindcss/typography": "^0.5.9",
66
72
  "@types/d3": "^7.4.0",
67
73
  "@types/dompurify": "^3.0.5",
68
74
  "@types/esri-leaflet": "^3.0.3",
69
75
  "@types/geojson": "^7946.0.10",
70
- "@types/leaflet": "^1.9.3",
76
+ "@types/leaflet": "^1.9.21",
71
77
  "@types/leaflet-draw": "^1.0.7",
72
78
  "@types/leaflet.markercluster": "^1.5.1",
73
79
  "@types/node": "18.11.18",
@@ -75,14 +81,11 @@
75
81
  "@types/react": "^18.2.0",
76
82
  "@types/react-dom": "^18.2.7",
77
83
  "@types/uuid": "^10.0.0",
84
+ "autoprefixer": "^10.4.14",
78
85
  "eslint": "^8.46.0",
79
86
  "postcss": "^8.4.21",
80
87
  "tailwindcss": "^3.3.3",
81
- "@tailwindcss/typography": "^0.5.9",
82
- "@tailwindcss/forms": "^0.5.3",
83
- "@tailwindcss/aspect-ratio": "^0.4.2",
84
88
  "tailwindcss-animate": "^1.0.7",
85
- "autoprefixer": "^10.4.14",
86
89
  "tsup": "^8.0.2",
87
90
  "typescript": "5.4.2",
88
91
  "eslint-config-custom": "0.0.0",
@@ -0,0 +1,5 @@
1
+ declare module "geojson-to-kml" {
2
+ export default function geojsonToKml(
3
+ geojson: GeoJSON.FeatureCollection | GeoJSON.Feature
4
+ ): string;
5
+ }
@@ -0,0 +1,20 @@
1
+ import { GeoJsonProperties } from "geojson";
2
+
3
+ export interface GeoJsonPropertiesProps {
4
+ properties: GeoJsonProperties;
5
+ isLoading?: boolean;
6
+ }
7
+
8
+ export interface ResearchSiteData {
9
+ id: number;
10
+ name: string;
11
+ country: string;
12
+ url: string;
13
+ established: string;
14
+ reference: string;
15
+ network: string;
16
+ PIs: string[];
17
+ PIs_text: string;
18
+ institutions: string[];
19
+ photos: string | null;
20
+ }
@@ -0,0 +1,44 @@
1
+ declare module "@bopen/leaflet-area-selection" {
2
+ import { Control, Map, LatLng, Polygon, Marker } from "leaflet";
3
+
4
+ export interface DrawAreaSelectionOptions {
5
+ active?: boolean;
6
+ fadeOnActivation?: boolean;
7
+ onPolygonReady?: (polygon: Polygon, control: DrawAreaSelection) => void;
8
+ onPolygonDblClick?: (
9
+ polygon: Polygon,
10
+ control: DrawAreaSelection,
11
+ event: Event,
12
+ ) => void;
13
+ onButtonActivate?: (control: DrawAreaSelection, event: Event) => void;
14
+ onButtonDeactivate?: (
15
+ polygon: Polygon,
16
+ control: DrawAreaSelection,
17
+ event: Event,
18
+ ) => void;
19
+ }
20
+
21
+ export class DrawAreaSelection extends Control {
22
+ constructor(options?: DrawAreaSelectionOptions);
23
+ onAdd(map: Map): HTMLElement;
24
+ onRemove(map: Map): void;
25
+ getMap(): Map;
26
+ onPolygonReady(): void;
27
+ onPolygonDblClick(ev: Event): void;
28
+ setPhase(phase: "inactive" | "draw" | "adjust", forceClear?: boolean): void;
29
+ clearMarkers(): void;
30
+ clearGhostMarkers(): void;
31
+ clearPolygon(): void;
32
+ deactivate(): void;
33
+ activate(): void;
34
+ active: boolean;
35
+ phase: "inactive" | "draw" | "adjust";
36
+ polygon: Polygon | null;
37
+ markers: Array<{ marker: Marker }>;
38
+ ghostMarkers: Array<{ marker: Marker }>;
39
+ }
40
+
41
+ export function drawAreaSelection(
42
+ options?: DrawAreaSelectionOptions,
43
+ ): DrawAreaSelection;
44
+ }
@@ -0,0 +1,98 @@
1
+ // src/types/leaflet-polylinemeasure.d.ts
2
+
3
+ import * as L from "leaflet";
4
+
5
+ declare module "leaflet" {
6
+ // Define the options interface
7
+ interface PolylineMeasureOptions extends L.ControlOptions {
8
+ unit?:
9
+ | "metres"
10
+ | "landmiles"
11
+ | "nauticalmiles"
12
+ | "feet"
13
+ | "inches"
14
+ | "yards";
15
+ showBearings?: boolean;
16
+ clearMeasurementsOnStop?: boolean;
17
+ showClearControl?: boolean;
18
+ showUnitControl?: boolean;
19
+ unitControlTitle?: object;
20
+ bearingTextIn?: string;
21
+ bearingTextOut?: string;
22
+ tooltipTextFinish?: string;
23
+ tooltipTextDelete?: string;
24
+ tooltipTextMove?: string;
25
+ tooltipTextResume?: string;
26
+ measureControlTitleOn?: string;
27
+ measureControlTitleOff?: string;
28
+ measureControlLabel?: string;
29
+ clearControlTitle?: string;
30
+ clearControlLabel?: string;
31
+ tempLine?:
32
+ | {
33
+ color?: string;
34
+ weight?: number;
35
+ }
36
+ | object;
37
+ fixedLine?:
38
+ | {
39
+ color?: string;
40
+ weight?: number;
41
+ }
42
+ | object;
43
+ startCircle?:
44
+ | {
45
+ color?: string;
46
+ }
47
+ | object;
48
+ intermedCircle?:
49
+ | {
50
+ color?: string;
51
+ }
52
+ | object;
53
+ currentCircle?: object;
54
+ endCircle?:
55
+ | {
56
+ color?: string;
57
+ }
58
+ | object;
59
+ }
60
+
61
+ // Define the control interface
62
+ interface PolylineMeasureControl extends L.Control {
63
+ _container: HTMLElement;
64
+ _toggleMeasure(): void;
65
+ _clearAllMeasurements(): void;
66
+ _measureControl: boolean;
67
+ _control?: {
68
+ _measuring: boolean;
69
+ _toggleMeasure: () => void;
70
+ };
71
+ }
72
+
73
+ function controlPolylineMeasure(
74
+ options?: PolylineMeasureOptions,
75
+ ): PolylineMeasureControl;
76
+
77
+ namespace control {
78
+ function polylineMeasure(
79
+ options?: PolylineMeasureOptions,
80
+ ): PolylineMeasureControl;
81
+ }
82
+ }
83
+
84
+ // Augment Leaflet namespace to properly expose the interface
85
+ declare global {
86
+ namespace L {
87
+ interface PolylineMeasureControl extends Control {
88
+ _container: HTMLElement;
89
+ _toggleMeasure(): void;
90
+ _clearAllMeasurements(): void;
91
+ _measureControl: boolean;
92
+ _control?: {
93
+ _measuring: boolean;
94
+ _toggleMeasure: () => void;
95
+ };
96
+ }
97
+ }
98
+ }
@@ -0,0 +1,16 @@
1
+ import { Point } from "leaflet";
2
+
3
+ declare module "leaflet" {
4
+ interface Map {
5
+ selectArea: {
6
+ enable(): void;
7
+ disable(): void;
8
+ setValidate(validateFn?: (point: Point) => boolean): void;
9
+ setControlKey(ctrlKey: boolean): void;
10
+ };
11
+ }
12
+
13
+ interface LeafletEvent {
14
+ bounds?: LatLngBounds;
15
+ }
16
+ }
@@ -0,0 +1,28 @@
1
+ export type ControlPosition =
2
+ | "topleft"
3
+ | "topright"
4
+ | "bottomleft"
5
+ | "bottomright";
6
+
7
+ export type PopoverDirection = "top" | "right" | "bottom" | "left";
8
+
9
+ export interface MapControl {
10
+ id: string;
11
+ title?: string;
12
+ icon: React.ComponentType<any>;
13
+ panel?: React.ReactNode;
14
+ onClick?: () => void;
15
+ position: ControlPosition;
16
+ isOpen: boolean;
17
+ popoverDirection?: PopoverDirection;
18
+ isMobileFilter?: boolean;
19
+ isDisplayOnly?: boolean;
20
+ }
21
+
22
+ export type BaseLayerType =
23
+ | "Esri World imagery"
24
+ | "OpenStreetMap"
25
+ | "Google Hybrid"
26
+ | "Google Satellite";
27
+ // | "Azure Satellite"
28
+ // | "Azure Satellite with Labels";
@@ -0,0 +1,10 @@
1
+ export interface MarkerPosition {
2
+ lat: number;
3
+ lng: number;
4
+ label?: string;
5
+ }
6
+
7
+ export interface LayerConfig {
8
+ layerName: string;
9
+ maxZoom: number;
10
+ }
@@ -0,0 +1 @@
1
+ declare module "@turf/turf";