@metastringfoundation/map-list 0.1.1 → 0.1.3
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/components/Handler/MapComparisonControl.d.ts +7 -0
- package/dist/components/Handler/MapComparisonControl.d.ts.map +1 -0
- package/dist/components/Handler/MapComparisonControl.js +8 -0
- package/dist/components/core/button.js +1 -1
- package/dist/components/core/input.d.ts.map +1 -1
- package/dist/components/core/input.js +1 -1
- package/dist/components/dualMap/MapLayerCard.d.ts +8 -0
- package/dist/components/dualMap/MapLayerCard.d.ts.map +1 -0
- package/dist/components/dualMap/MapLayerCard.js +68 -0
- package/dist/components/dualMap/index.d.ts +2 -1
- package/dist/components/dualMap/index.d.ts.map +1 -1
- package/dist/components/dualMap/index.js +219 -56
- package/dist/components/map/compare-sidebar.d.ts +2 -0
- package/dist/components/map/compare-sidebar.d.ts.map +1 -0
- package/dist/components/map/compare-sidebar.js +34 -0
- package/dist/components/map/compare.d.ts +2 -0
- package/dist/components/map/compare.d.ts.map +1 -0
- package/dist/components/map/compare.js +42 -0
- package/dist/components/map/index.d.ts.map +1 -1
- package/dist/components/map/index.js +14 -1
- package/dist/components/map/layers/grid/index.d.ts.map +1 -1
- package/dist/components/map/layers/grid/index.js +11 -1
- package/dist/components/map/layers/index.d.ts.map +1 -1
- package/dist/components/map/layers/index.js +0 -1
- package/dist/components/map/layers/raster/index.d.ts.map +1 -1
- package/dist/components/map/layers/raster/index.js +5 -3
- package/dist/components/map/layers/vector/index.d.ts.map +1 -1
- package/dist/components/map/layers/vector/index.js +25 -2
- package/dist/components/map/search.d.ts +2 -0
- package/dist/components/map/search.d.ts.map +1 -0
- package/dist/components/map/search.js +122 -0
- package/dist/components/map-comparison/attribute-selector.d.ts +11 -0
- package/dist/components/map-comparison/attribute-selector.d.ts.map +1 -0
- package/dist/components/map-comparison/attribute-selector.js +10 -0
- package/dist/components/map-comparison/comparison-panel.d.ts +2 -0
- package/dist/components/map-comparison/comparison-panel.d.ts.map +1 -0
- package/dist/components/map-comparison/comparison-panel.js +45 -0
- package/dist/components/map-comparison/comparison-stats.d.ts +2 -0
- package/dist/components/map-comparison/comparison-stats.d.ts.map +1 -0
- package/dist/components/map-comparison/comparison-stats.js +35 -0
- package/dist/components/map-comparison/comparison-view.d.ts +2 -0
- package/dist/components/map-comparison/comparison-view.d.ts.map +1 -0
- package/dist/components/map-comparison/comparison-view.js +152 -0
- package/dist/components/map-comparison/comparison-wrapper.d.ts +8 -0
- package/dist/components/map-comparison/comparison-wrapper.d.ts.map +1 -0
- package/dist/components/map-comparison/comparison-wrapper.js +26 -0
- package/dist/components/map-comparison/index.d.ts +3 -0
- package/dist/components/map-comparison/index.d.ts.map +1 -0
- package/dist/components/map-comparison/index.js +165 -0
- package/dist/components/map-comparison/types.d.ts +30 -0
- package/dist/components/map-comparison/types.d.ts.map +1 -0
- package/dist/components/map-comparison/types.js +2 -0
- package/dist/components/map-comparison/use-map-comparison.d.ts +18 -0
- package/dist/components/map-comparison/use-map-comparison.d.ts.map +1 -0
- package/dist/components/map-comparison/use-map-comparison.js +139 -0
- package/dist/components/sidebar/common/attribute-compare-addon.d.ts +7 -0
- package/dist/components/sidebar/common/attribute-compare-addon.d.ts.map +1 -0
- package/dist/components/sidebar/common/attribute-compare-addon.js +36 -0
- package/dist/components/sidebar/common/layer-item-style.d.ts.map +1 -1
- package/dist/components/sidebar/common/layer-item-style.js +46 -1
- package/dist/components/sidebar/common/layer-item.d.ts.map +1 -1
- package/dist/components/sidebar/common/layer-item.js +1 -2
- package/dist/components/sidebar/common/opacity-handler-addon.d.ts +5 -2
- package/dist/components/sidebar/common/opacity-handler-addon.d.ts.map +1 -1
- package/dist/components/sidebar/common/opacity-handler-addon.js +101 -4
- package/dist/components/sidebar/common/style-legend.d.ts.map +1 -1
- package/dist/components/sidebar/common/style-legend.js +1 -2
- package/dist/components/sidebar/comparison/index.d.ts +4 -0
- package/dist/components/sidebar/comparison/index.d.ts.map +1 -0
- package/dist/components/sidebar/comparison/index.js +34 -0
- package/dist/components/sidebar/search/attribute-comparison.d.ts +10 -0
- package/dist/components/sidebar/search/attribute-comparison.d.ts.map +1 -0
- package/dist/components/sidebar/search/attribute-comparison.js +137 -0
- package/dist/components/sidebar/search/index.d.ts +2 -0
- package/dist/components/sidebar/search/index.d.ts.map +1 -0
- package/dist/components/sidebar/search/index.js +29 -0
- package/dist/components/sidebar/selected/index.d.ts.map +1 -1
- package/dist/components/sidebar/selected/index.js +4 -1
- package/dist/components/sidebar/tabs.js +2 -2
- package/dist/hooks/use-layers.d.ts +8 -0
- package/dist/hooks/use-layers.d.ts.map +1 -1
- package/dist/hooks/use-layers.js +101 -1
- package/dist/hooks/use-polygon-data.d.ts +34 -0
- package/dist/hooks/use-polygon-data.d.ts.map +1 -0
- package/dist/hooks/use-polygon-data.js +74 -0
- package/dist/index.css +659 -30
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -36
- package/dist/services/naksha.d.ts.map +1 -1
- package/dist/services/naksha.js +16 -158
- package/dist/services/polygon.d.ts +36 -0
- package/dist/services/polygon.d.ts.map +1 -0
- package/dist/services/polygon.js +67 -0
- package/dist/utils/naksha.d.ts.map +1 -1
- package/dist/utils/naksha.js +4 -2
- package/package.json +16 -15
|
@@ -27,5 +27,15 @@ function GridLayer({ data, beforeId, }) {
|
|
|
27
27
|
(0, react_1.useEffect)(() => {
|
|
28
28
|
mapl?.on("idle", fetchGridData);
|
|
29
29
|
}, []);
|
|
30
|
-
|
|
30
|
+
const opacity = layer.getLayerOpacity(data.id);
|
|
31
|
+
// Merge opacity into grid layer paint
|
|
32
|
+
const paintWithOpacity = (0, react_1.useMemo)(() => {
|
|
33
|
+
if (!layerData.paint)
|
|
34
|
+
return layerData.paint;
|
|
35
|
+
return {
|
|
36
|
+
...layerData.paint,
|
|
37
|
+
"fill-opacity": opacity,
|
|
38
|
+
};
|
|
39
|
+
}, [layerData.paint, opacity]);
|
|
40
|
+
return ((0, jsx_runtime_1.jsx)(maplibre_1.Source, { id: data.id, type: "geojson", data: layerData.geojson, children: (0, jsx_runtime_1.jsx)(maplibre_1.Layer, { beforeId: beforeId, id: data.id, type: "fill", paint: paintWithOpacity }) }));
|
|
31
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/map/layers/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAKrD,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,cAAc,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/map/layers/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAKrD,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,cAAc,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,kDAqBA"}
|
|
@@ -10,7 +10,6 @@ const grid_1 = __importDefault(require("./grid"));
|
|
|
10
10
|
const vector_1 = __importDefault(require("./vector"));
|
|
11
11
|
const raster_1 = __importDefault(require("./raster"));
|
|
12
12
|
function MapLayer({ layer, beforeId, }) {
|
|
13
|
-
console.log("layer.source.type", layer?.source?.type);
|
|
14
13
|
const maps = (0, maplibre_1.useMap)();
|
|
15
14
|
// if (!mapl) {
|
|
16
15
|
// return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/map/layers/raster/index.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,CAAC,EAAC,MAAM,CAAA;CACjB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/map/layers/raster/index.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,CAAC,EAAC,MAAM,CAAA;CACjB,2CA6BA"}
|
|
@@ -8,10 +8,12 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
8
8
|
const maplibre_1 = require("react-map-gl/maplibre");
|
|
9
9
|
const use_layers_1 = __importDefault(require("../../../../hooks/use-layers"));
|
|
10
10
|
function RasterLayer({ data, beforeId, }) {
|
|
11
|
-
const { mp } = (0, use_layers_1.default)();
|
|
12
|
-
|
|
11
|
+
const { mp, layer } = (0, use_layers_1.default)();
|
|
12
|
+
const opacity = layer.getLayerOpacity(data.id);
|
|
13
13
|
return ((0, jsx_runtime_1.jsx)(maplibre_1.Source, { id: data.id, type: "raster", tiles: [
|
|
14
14
|
// @ts-ignore
|
|
15
15
|
`${mp?.geoserver?.endpoint}/wms?bbox={bbox-epsg-3857}&format=image/png&service=WMS&version=1.1.1&request=GetMap&srs=EPSG:3857&width=256&height=256&transparent=true&layers=metastring:${data.name}`,
|
|
16
|
-
], tileSize: 256, children: (0, jsx_runtime_1.jsx)(maplibre_1.Layer, { id: data.id, beforeId: beforeId, type: "raster", source: "biodiv", layout: { visibility: "visible" }, paint: {
|
|
16
|
+
], tileSize: 256, children: (0, jsx_runtime_1.jsx)(maplibre_1.Layer, { id: data.id, beforeId: beforeId, type: "raster", source: "biodiv", layout: { visibility: "visible" }, paint: {
|
|
17
|
+
"raster-opacity": opacity,
|
|
18
|
+
} }) }));
|
|
17
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/map/layers/vector/index.tsx"],"names":[],"mappings":"AAmBA,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE;;;CAAA,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/map/layers/vector/index.tsx"],"names":[],"mappings":"AAmBA,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE;;;CAAA,2CA8D5D"}
|
|
@@ -24,7 +24,30 @@ const paint = {
|
|
|
24
24
|
function VectorLayer({ layer: data, beforeId }) {
|
|
25
25
|
const { layer } = (0, use_layers_1.default)();
|
|
26
26
|
const layerProps = (0, react_1.useMemo)(() => data.data?.styles?.[data.data?.styleIndex]?.colors, [data, layer.selectedFeatures]);
|
|
27
|
+
const opacity = layer.getLayerOpacity(data.id);
|
|
27
28
|
const highlightData = layer.selectedFeaturesId?.[data.id];
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
// Merge opacity into layer paint properties based on layer type
|
|
30
|
+
const layerPaintWithOpacity = (0, react_1.useMemo)(() => {
|
|
31
|
+
if (!layerProps?.type)
|
|
32
|
+
return layerProps?.paint;
|
|
33
|
+
const paint = layerProps.paint ? { ...layerProps.paint } : {};
|
|
34
|
+
const layerType = layerProps.type;
|
|
35
|
+
// Apply opacity based on layer type
|
|
36
|
+
if (layerType === "fill") {
|
|
37
|
+
paint["fill-opacity"] = opacity;
|
|
38
|
+
}
|
|
39
|
+
else if (layerType === "line") {
|
|
40
|
+
paint["line-opacity"] = opacity;
|
|
41
|
+
}
|
|
42
|
+
else if (layerType === "circle") {
|
|
43
|
+
paint["circle-opacity"] = opacity;
|
|
44
|
+
}
|
|
45
|
+
else if (layerType === "symbol") {
|
|
46
|
+
paint["icon-opacity"] = opacity;
|
|
47
|
+
paint["text-opacity"] = opacity;
|
|
48
|
+
}
|
|
49
|
+
return paint;
|
|
50
|
+
}, [layerProps, opacity]);
|
|
51
|
+
const baseId = data?.id;
|
|
52
|
+
return ((0, jsx_runtime_1.jsxs)(maplibre_1.Source, { id: baseId, ...data.source, children: [layerProps && ((0, jsx_runtime_1.jsx)(maplibre_1.Layer, { beforeId: beforeId, id: baseId, ...layerProps, paint: layerPaintWithOpacity !== undefined ? layerPaintWithOpacity : layerProps.paint })), highlightData && layerProps && ((0, jsx_runtime_1.jsx)(maplibre_1.Layer, { ...layerProps, beforeId: beforeId, id: `hl_${baseId}`, filter: ["in", constants_1.PROPERTY_ID, ...highlightData], type: layerProps.type === "fill" ? "line" : layerProps.type, paint: paint[layerProps.type] }))] }));
|
|
30
53
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../../src/components/map/search.tsx"],"names":[],"mappings":"AAUA,MAAM,CAAC,OAAO,UAAU,SAAS,4CA6OhC"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = MapSearch;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const maplibre_1 = require("react-map-gl/maplibre");
|
|
7
|
+
const core_1 = require("../core");
|
|
8
|
+
function MapSearch() {
|
|
9
|
+
const { mapl } = (0, maplibre_1.useMap)();
|
|
10
|
+
const [searchTerm, setSearchTerm] = (0, react_1.useState)("");
|
|
11
|
+
const [results, setResults] = (0, react_1.useState)([]);
|
|
12
|
+
const [isSearching, setIsSearching] = (0, react_1.useState)(false);
|
|
13
|
+
const [showResults, setShowResults] = (0, react_1.useState)(false);
|
|
14
|
+
const searchTimeoutRef = (0, react_1.useRef)(null);
|
|
15
|
+
const resultsRef = (0, react_1.useRef)(null);
|
|
16
|
+
// Close results when clicking outside
|
|
17
|
+
(0, react_1.useEffect)(() => {
|
|
18
|
+
const handleClickOutside = (event) => {
|
|
19
|
+
if (resultsRef.current && !resultsRef.current.contains(event.target)) {
|
|
20
|
+
setShowResults(false);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
24
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
25
|
+
}, []);
|
|
26
|
+
const handleSearch = async (query) => {
|
|
27
|
+
if (!query.trim()) {
|
|
28
|
+
setResults([]);
|
|
29
|
+
setShowResults(false);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
setIsSearching(true);
|
|
33
|
+
try {
|
|
34
|
+
// Using Nominatim (OpenStreetMap) geocoding service as a free alternative
|
|
35
|
+
// You can replace this with Mapbox Geocoding API or any other service
|
|
36
|
+
const response = await fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(query)}&limit=5`, {
|
|
37
|
+
headers: {
|
|
38
|
+
"User-Agent": "MapSearch/1.0", // Required by Nominatim
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
throw new Error("Geocoding request failed");
|
|
43
|
+
}
|
|
44
|
+
const data = await response.json();
|
|
45
|
+
const formattedResults = data.map((item) => ({
|
|
46
|
+
place_name: item.display_name,
|
|
47
|
+
center: [parseFloat(item.lon), parseFloat(item.lat)],
|
|
48
|
+
bbox: item.boundingbox
|
|
49
|
+
? [
|
|
50
|
+
parseFloat(item.boundingbox[2]), // min lat
|
|
51
|
+
parseFloat(item.boundingbox[0]), // min lon
|
|
52
|
+
parseFloat(item.boundingbox[3]), // max lat
|
|
53
|
+
parseFloat(item.boundingbox[1]), // max lon
|
|
54
|
+
]
|
|
55
|
+
: undefined,
|
|
56
|
+
}));
|
|
57
|
+
setResults(formattedResults);
|
|
58
|
+
setShowResults(true);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
console.error("Error searching location:", error);
|
|
62
|
+
setResults([]);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
setIsSearching(false);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const handleInputChange = (e) => {
|
|
69
|
+
const value = e.target.value;
|
|
70
|
+
setSearchTerm(value);
|
|
71
|
+
// Debounce search
|
|
72
|
+
if (searchTimeoutRef.current) {
|
|
73
|
+
clearTimeout(searchTimeoutRef.current);
|
|
74
|
+
}
|
|
75
|
+
if (value.trim()) {
|
|
76
|
+
searchTimeoutRef.current = setTimeout(() => {
|
|
77
|
+
handleSearch(value);
|
|
78
|
+
}, 500);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
setResults([]);
|
|
82
|
+
setShowResults(false);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const handleSelectResult = (result) => {
|
|
86
|
+
if (!mapl)
|
|
87
|
+
return;
|
|
88
|
+
const mapInstance = Object.values(mapl).pop();
|
|
89
|
+
if (!mapInstance)
|
|
90
|
+
return;
|
|
91
|
+
setSearchTerm(result.place_name);
|
|
92
|
+
setShowResults(false);
|
|
93
|
+
// Fly to the location
|
|
94
|
+
if (result.bbox) {
|
|
95
|
+
// Fit to bounding box if available
|
|
96
|
+
mapInstance.fitBounds([
|
|
97
|
+
[result.bbox[1], result.bbox[0]], // southwest
|
|
98
|
+
[result.bbox[3], result.bbox[2]], // northeast
|
|
99
|
+
], {
|
|
100
|
+
padding: 50,
|
|
101
|
+
duration: 1500,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Fly to center point
|
|
106
|
+
mapInstance.flyTo({
|
|
107
|
+
center: result.center,
|
|
108
|
+
zoom: 12,
|
|
109
|
+
duration: 1500,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const handleClear = () => {
|
|
114
|
+
setSearchTerm("");
|
|
115
|
+
setResults([]);
|
|
116
|
+
setShowResults(false);
|
|
117
|
+
if (searchTimeoutRef.current) {
|
|
118
|
+
clearTimeout(searchTimeoutRef.current);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "absolute top-4 left-1/2 transform -translate-x-1/2 z-30 w-full max-w-md px-4", children: (0, jsx_runtime_1.jsxs)("div", { className: "relative", ref: resultsRef, children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsx)(core_1.SearchInput, { type: "search", placeholder: "Search for a location...", value: searchTerm, onChange: handleInputChange, className: "w-full shadow-lg" }), isSearching && ((0, jsx_runtime_1.jsx)("div", { className: "absolute right-3 top-1/2 transform -translate-y-1/2", children: (0, jsx_runtime_1.jsxs)("svg", { className: "animate-spin h-5 w-5 text-gray-400", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), (0, jsx_runtime_1.jsx)("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] }) })), searchTerm && !isSearching && ((0, jsx_runtime_1.jsx)("button", { onClick: handleClear, className: "absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600", type: "button", children: (0, jsx_runtime_1.jsx)("svg", { className: "h-5 w-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M6 18L18 6M6 6l12 12" }) }) }))] }), showResults && results.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "absolute top-full left-0 right-0 mt-1 bg-white rounded-md shadow-lg border border-gray-200 max-h-64 overflow-y-auto z-50", children: results.map((result, index) => ((0, jsx_runtime_1.jsx)("button", { onClick: () => handleSelectResult(result), className: "w-full text-left px-4 py-3 hover:bg-gray-50 border-b border-gray-100 last:border-b-0 transition-colors", type: "button", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-start gap-2", children: [(0, jsx_runtime_1.jsxs)("svg", { className: "w-5 h-5 text-gray-400 mt-0.5 shrink-0", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: [(0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" }), (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M15 11a3 3 0 11-6 0 3 3 0 016 0z" })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex-1 min-w-0", children: (0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-gray-800 truncate", children: result.place_name }) })] }) }, index))) })), showResults && results.length === 0 && searchTerm && !isSearching && ((0, jsx_runtime_1.jsx)("div", { className: "absolute top-full left-0 right-0 mt-1 bg-white rounded-md shadow-lg border border-gray-200 p-4 z-50", children: (0, jsx_runtime_1.jsxs)("p", { className: "text-sm text-gray-500 text-center", children: ["No results found for \"", searchTerm, "\""] }) }))] }) }));
|
|
122
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AttributeOption } from "./types";
|
|
2
|
+
interface AttributeSelectorProps {
|
|
3
|
+
layerId: string;
|
|
4
|
+
attributes: AttributeOption[];
|
|
5
|
+
selectedAttribute?: string;
|
|
6
|
+
onSelect: (attributeKey?: string) => void;
|
|
7
|
+
label?: string;
|
|
8
|
+
}
|
|
9
|
+
export default function AttributeSelector({ layerId, attributes, selectedAttribute, onSelect, label, }: AttributeSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=attribute-selector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribute-selector.d.ts","sourceRoot":"","sources":["../../../src/components/map-comparison/attribute-selector.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C,UAAU,sBAAsB;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,EACxC,OAAO,EACP,UAAU,EACV,iBAAiB,EACjB,QAAQ,EACR,KAA0B,GAC3B,EAAE,sBAAsB,2CA0BxB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = AttributeSelector;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
function AttributeSelector({ layerId, attributes, selectedAttribute, onSelect, label = "Select Attribute", }) {
|
|
6
|
+
if (attributes.length === 0) {
|
|
7
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "text-sm text-gray-500", children: "No attributes available for this layer" }));
|
|
8
|
+
}
|
|
9
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-sm font-medium text-gray-700", children: label }), (0, jsx_runtime_1.jsxs)("select", { value: selectedAttribute || "", onChange: (e) => onSelect(e.target.value || undefined), className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm", children: [(0, jsx_runtime_1.jsx)("option", { value: "", children: "Default (All Attributes)" }), attributes.map((attr) => ((0, jsx_runtime_1.jsxs)("option", { value: attr.key, children: [attr.label, " (", attr.type, ")"] }, attr.key)))] })] }));
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparison-panel.d.ts","sourceRoot":"","sources":["../../../src/components/map-comparison/comparison-panel.tsx"],"names":[],"mappings":"AAOA,MAAM,CAAC,OAAO,UAAU,eAAe,4CAyQtC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.default = ComparisonPanel;
|
|
8
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
9
|
+
const react_1 = require("react");
|
|
10
|
+
const use_map_comparison_1 = require("./use-map-comparison");
|
|
11
|
+
const attribute_selector_1 = __importDefault(require("./attribute-selector"));
|
|
12
|
+
function ComparisonPanel() {
|
|
13
|
+
const { isOpen, setIsOpen, config, availableLayers, getLayerAttributes, addLayer, removeLayer, updateLayerAttribute, setMode, toggleSyncView, toggleStats, clearComparison, isLayerInComparison, } = (0, use_map_comparison_1.useMapComparison)();
|
|
14
|
+
const [selectedLayerId, setSelectedLayerId] = (0, react_1.useState)("");
|
|
15
|
+
const [selectedAttribute, setSelectedAttribute] = (0, react_1.useState)("");
|
|
16
|
+
if (!isOpen) {
|
|
17
|
+
return ((0, jsx_runtime_1.jsxs)("button", { onClick: () => setIsOpen(true), className: "fixed bottom-4 left-4 z-50 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg shadow-lg flex items-center gap-2 font-medium", children: [(0, jsx_runtime_1.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [(0, jsx_runtime_1.jsx)("path", { d: "M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" }), (0, jsx_runtime_1.jsx)("polyline", { points: "3.27 6.96 12 12.01 20.73 6.96" }), (0, jsx_runtime_1.jsx)("line", { x1: "12", y1: "22.08", x2: "12", y2: "12" })] }), "Map Comparison"] }));
|
|
18
|
+
}
|
|
19
|
+
const handleAddLayer = () => {
|
|
20
|
+
if (!selectedLayerId)
|
|
21
|
+
return;
|
|
22
|
+
const attributes = getLayerAttributes(selectedLayerId);
|
|
23
|
+
const attributeKey = selectedAttribute || undefined;
|
|
24
|
+
addLayer(selectedLayerId, attributeKey);
|
|
25
|
+
setSelectedLayerId("");
|
|
26
|
+
setSelectedAttribute("");
|
|
27
|
+
};
|
|
28
|
+
const selectedLayerAttributes = selectedLayerId
|
|
29
|
+
? getLayerAttributes(selectedLayerId)
|
|
30
|
+
: [];
|
|
31
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "fixed bottom-4 left-4 z-50 w-96 bg-white rounded-lg shadow-2xl border border-gray-200 max-h-[80vh] overflow-y-auto", children: [(0, jsx_runtime_1.jsxs)("div", { className: "sticky top-0 bg-blue-600 text-white p-4 rounded-t-lg flex justify-between items-center", children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-semibold", children: "Map Comparison" }), (0, jsx_runtime_1.jsx)("button", { onClick: () => setIsOpen(false), className: "text-white hover:text-gray-200", children: (0, jsx_runtime_1.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [(0, jsx_runtime_1.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), (0, jsx_runtime_1.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "p-4 space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-sm font-medium text-gray-700", children: "Comparison Mode" }), (0, jsx_runtime_1.jsx)("div", { className: "flex gap-2", children: ["side-by-side", "overlay", "attribute"].map((mode) => ((0, jsx_runtime_1.jsx)("button", { onClick: () => setMode(mode), className: `px-3 py-1 rounded-md text-sm font-medium transition-colors ${config.mode === mode
|
|
32
|
+
? "bg-blue-600 text-white"
|
|
33
|
+
: "bg-gray-100 text-gray-700 hover:bg-gray-200"}`, children: mode
|
|
34
|
+
.split("-")
|
|
35
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
36
|
+
.join(" ") }, mode))) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "border-t pt-4", children: [(0, jsx_runtime_1.jsx)("h4", { className: "text-sm font-semibold text-gray-700 mb-3", children: "Add Layer to Compare" }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-sm font-medium text-gray-700", children: "Select Layer" }), (0, jsx_runtime_1.jsxs)("select", { value: selectedLayerId, onChange: (e) => {
|
|
37
|
+
setSelectedLayerId(e.target.value);
|
|
38
|
+
setSelectedAttribute("");
|
|
39
|
+
}, className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm", children: [(0, jsx_runtime_1.jsx)("option", { value: "", children: "Choose a layer..." }), availableLayers
|
|
40
|
+
.filter((l) => !isLayerInComparison(l.id))
|
|
41
|
+
.map((layer) => ((0, jsx_runtime_1.jsx)("option", { value: layer.id, children: layer.title }, layer.id)))] })] }), selectedLayerId && ((0, jsx_runtime_1.jsx)(attribute_selector_1.default, { layerId: selectedLayerId, attributes: selectedLayerAttributes, selectedAttribute: selectedAttribute, onSelect: setSelectedAttribute, label: "Select Attribute (Optional)" })), (0, jsx_runtime_1.jsx)("button", { onClick: handleAddLayer, disabled: !selectedLayerId, className: "w-full px-4 py-2 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-300 disabled:cursor-not-allowed text-white rounded-md text-sm font-medium transition-colors", children: "Add to Comparison" })] })] }), config.layers.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "border-t pt-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center mb-3", children: [(0, jsx_runtime_1.jsxs)("h4", { className: "text-sm font-semibold text-gray-700", children: ["Comparison Layers (", config.layers.length, ")"] }), (0, jsx_runtime_1.jsx)("button", { onClick: clearComparison, className: "text-xs text-red-600 hover:text-red-700", children: "Clear All" })] }), (0, jsx_runtime_1.jsx)("div", { className: "space-y-2", children: config.layers.map((compLayer) => {
|
|
42
|
+
const attributes = getLayerAttributes(compLayer.layerId);
|
|
43
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "p-3 bg-gray-50 rounded-md border border-gray-200", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-start mb-2", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex-1", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm font-medium text-gray-800", children: compLayer.layer.title }), compLayer.attributeLabel && ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-gray-600 mt-1", children: ["Attribute: ", compLayer.attributeLabel] }))] }), (0, jsx_runtime_1.jsx)("button", { onClick: () => removeLayer(compLayer.layerId), className: "text-red-600 hover:text-red-700 ml-2", children: (0, jsx_runtime_1.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [(0, jsx_runtime_1.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), (0, jsx_runtime_1.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })] }), attributes.length > 0 && ((0, jsx_runtime_1.jsx)(attribute_selector_1.default, { layerId: compLayer.layerId, attributes: attributes, selectedAttribute: compLayer.attributeKey, onSelect: (attrKey) => updateLayerAttribute(compLayer.layerId, attrKey), label: "Change Attribute" }))] }, compLayer.layerId));
|
|
44
|
+
}) })] })), config.layers.length > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "border-t pt-4 space-y-2", children: [(0, jsx_runtime_1.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: config.syncView, onChange: toggleSyncView, className: "h-4 w-4 accent-blue-600" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm text-gray-700", children: "Sync Map Views" })] }), (0, jsx_runtime_1.jsxs)("label", { className: "flex items-center gap-2 cursor-pointer", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: config.showStats, onChange: toggleStats, className: "h-4 w-4 accent-blue-600" }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm text-gray-700", children: "Show Statistics" })] })] }))] })] }));
|
|
45
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparison-stats.d.ts","sourceRoot":"","sources":["../../../src/components/map-comparison/comparison-stats.tsx"],"names":[],"mappings":"AAMA,MAAM,CAAC,OAAO,UAAU,eAAe,mDAuGtC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.default = ComparisonStats;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const use_map_comparison_1 = require("./use-map-comparison");
|
|
8
|
+
function ComparisonStats() {
|
|
9
|
+
const { config } = (0, use_map_comparison_1.useMapComparison)();
|
|
10
|
+
// Calculate basic statistics for each layer
|
|
11
|
+
const stats = (0, react_1.useMemo)(() => {
|
|
12
|
+
return config.layers.map((compLayer) => {
|
|
13
|
+
// This is a placeholder - in a real implementation, you would:
|
|
14
|
+
// 1. Query the layer data for the selected attribute
|
|
15
|
+
// 2. Calculate min, max, avg, unique values, etc.
|
|
16
|
+
// 3. For now, we'll show basic layer info
|
|
17
|
+
const stats = {
|
|
18
|
+
layerId: compLayer.layerId,
|
|
19
|
+
attributeKey: compLayer.attributeKey,
|
|
20
|
+
totalFeatures: undefined, // Would need to query layer
|
|
21
|
+
minValue: undefined,
|
|
22
|
+
maxValue: undefined,
|
|
23
|
+
avgValue: undefined,
|
|
24
|
+
uniqueValues: undefined,
|
|
25
|
+
};
|
|
26
|
+
return stats;
|
|
27
|
+
});
|
|
28
|
+
}, [config.layers]);
|
|
29
|
+
if (config.layers.length === 0)
|
|
30
|
+
return null;
|
|
31
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "absolute top-4 right-4 z-50 bg-white rounded-lg shadow-lg border border-gray-200 p-4 max-w-sm", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center mb-3", children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-sm font-semibold text-gray-800", children: "Comparison Statistics" }), (0, jsx_runtime_1.jsx)("button", { className: "text-gray-400 hover:text-gray-600", children: (0, jsx_runtime_1.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [(0, jsx_runtime_1.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), (0, jsx_runtime_1.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })] }), (0, jsx_runtime_1.jsx)("div", { className: "space-y-3", children: config.layers.map((compLayer, index) => {
|
|
32
|
+
const layerStats = stats[index];
|
|
33
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "p-3 bg-gray-50 rounded-md border border-gray-200", children: [(0, jsx_runtime_1.jsx)("h4", { className: "text-sm font-medium text-gray-800 mb-2", children: compLayer.layer.title }), compLayer.attributeLabel && ((0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-gray-600 mb-2", children: ["Attribute: ", compLayer.attributeLabel] })), (0, jsx_runtime_1.jsxs)("div", { className: "text-xs text-gray-500 space-y-1", children: [layerStats.totalFeatures !== undefined && ((0, jsx_runtime_1.jsxs)("div", { children: ["Total Features: ", layerStats.totalFeatures.toLocaleString()] })), layerStats.minValue !== undefined && ((0, jsx_runtime_1.jsxs)("div", { children: ["Min: ", layerStats.minValue] })), layerStats.maxValue !== undefined && ((0, jsx_runtime_1.jsxs)("div", { children: ["Max: ", layerStats.maxValue] })), layerStats.avgValue !== undefined && ((0, jsx_runtime_1.jsxs)("div", { children: ["Average: ", layerStats.avgValue.toFixed(2)] })), layerStats.uniqueValues !== undefined && ((0, jsx_runtime_1.jsxs)("div", { children: ["Unique Values: ", layerStats.uniqueValues] })), !layerStats.totalFeatures && ((0, jsx_runtime_1.jsx)("div", { className: "text-gray-400 italic", children: "Statistics calculation not implemented" }))] })] }, compLayer.layerId));
|
|
34
|
+
}) }), config.mode === "attribute" && ((0, jsx_runtime_1.jsx)("div", { className: "mt-3 pt-3 border-t border-gray-200", children: (0, jsx_runtime_1.jsxs)("p", { className: "text-xs text-gray-600", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Attribute Comparison Mode:" }), " Comparing different attributes across layers. Use this to analyze how different attributes relate to each other."] }) }))] }));
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparison-view.d.ts","sourceRoot":"","sources":["../../../src/components/map-comparison/comparison-view.tsx"],"names":[],"mappings":"AAkBA,MAAM,CAAC,OAAO,UAAU,cAAc,4CA0KrC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.default = ComparisonView;
|
|
41
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
42
|
+
const react_1 = require("react");
|
|
43
|
+
const maplibre_1 = __importStar(require("react-map-gl/maplibre"));
|
|
44
|
+
const map_common_1 = require("@metastringfoundation/map-common");
|
|
45
|
+
const use_map_comparison_1 = require("./use-map-comparison");
|
|
46
|
+
const layers_1 = require("../map/layers");
|
|
47
|
+
const popup_1 = __importDefault(require("../map/popup"));
|
|
48
|
+
const use_layers_1 = __importDefault(require("../../hooks/use-layers"));
|
|
49
|
+
const comparison_stats_1 = __importDefault(require("./comparison-stats"));
|
|
50
|
+
const NavControl = maplibre_1.NavigationControl;
|
|
51
|
+
function ComparisonView() {
|
|
52
|
+
const { config } = (0, use_map_comparison_1.useMapComparison)();
|
|
53
|
+
const { mp, layer } = (0, use_layers_1.default)();
|
|
54
|
+
const [coordsByPane, setCoordsByPane] = (0, react_1.useState)({});
|
|
55
|
+
const [viewStates, setViewStates] = (0, react_1.useState)({});
|
|
56
|
+
const initialViewState = (0, react_1.useMemo)(() => mp.defaultViewState ?? map_common_1.defaultViewState, [mp.defaultViewState]);
|
|
57
|
+
// Initialize view states
|
|
58
|
+
(0, react_1.useEffect)(() => {
|
|
59
|
+
if (config.layers.length > 0) {
|
|
60
|
+
const newViewStates = {};
|
|
61
|
+
config.layers.forEach((compLayer) => {
|
|
62
|
+
if (!viewStates[compLayer.layerId]) {
|
|
63
|
+
newViewStates[compLayer.layerId] = initialViewState;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
if (Object.keys(newViewStates).length > 0) {
|
|
67
|
+
setViewStates((prev) => ({ ...prev, ...newViewStates }));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}, [config.layers, initialViewState]);
|
|
71
|
+
// Sync view states if enabled
|
|
72
|
+
const handleViewStateChange = (layerId, viewState) => {
|
|
73
|
+
if (config.syncView && config.layers.length > 1) {
|
|
74
|
+
// Sync all maps to the same view state
|
|
75
|
+
const syncedViewStates = {};
|
|
76
|
+
config.layers.forEach((l) => {
|
|
77
|
+
syncedViewStates[l.layerId] = viewState;
|
|
78
|
+
});
|
|
79
|
+
setViewStates((prev) => ({ ...prev, ...syncedViewStates }));
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// Update only the specific map
|
|
83
|
+
setViewStates((prev) => ({ ...prev, [layerId]: viewState }));
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const onMouseMove = (layerId) => (e) => {
|
|
87
|
+
if (!e.lngLat)
|
|
88
|
+
return;
|
|
89
|
+
setCoordsByPane((prev) => ({
|
|
90
|
+
...prev,
|
|
91
|
+
[layerId]: e.lngLat,
|
|
92
|
+
}));
|
|
93
|
+
};
|
|
94
|
+
if (config.layers.length === 0) {
|
|
95
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: "w-full h-screen flex items-center justify-center bg-gray-100", children: (0, jsx_runtime_1.jsxs)("div", { className: "text-center", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-gray-500 text-lg mb-2", children: "No layers selected for comparison" }), (0, jsx_runtime_1.jsx)("p", { className: "text-gray-400 text-sm", children: "Add layers from the comparison panel to get started" })] }) }));
|
|
96
|
+
}
|
|
97
|
+
// Prepare layers with attribute/style modifications
|
|
98
|
+
const preparedLayers = (0, react_1.useMemo)(() => {
|
|
99
|
+
return config.layers.map((compLayer) => {
|
|
100
|
+
const baseLayer = { ...compLayer.layer };
|
|
101
|
+
// If attribute-based comparison, modify the layer to show specific attribute
|
|
102
|
+
if (compLayer.attributeKey && compLayer.attributeKey.startsWith("style_")) {
|
|
103
|
+
const styleIndex = parseInt(compLayer.attributeKey.replace("style_", ""));
|
|
104
|
+
if (baseLayer.data) {
|
|
105
|
+
baseLayer.data = {
|
|
106
|
+
...baseLayer.data,
|
|
107
|
+
styleIndex: styleIndex,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
...baseLayer,
|
|
113
|
+
id: `compare-${compLayer.layerId}`,
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
}, [config.layers]);
|
|
117
|
+
// Render based on mode
|
|
118
|
+
if (config.mode === "overlay") {
|
|
119
|
+
// Overlay mode: stack all layers on a single map
|
|
120
|
+
const overlayViewState = viewStates[config.layers[0]?.layerId] || initialViewState;
|
|
121
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "w-full h-screen relative", children: [config.showStats && (0, jsx_runtime_1.jsx)(comparison_stats_1.default, {}), (0, jsx_runtime_1.jsxs)(maplibre_1.default, { id: "comparison-overlay", initialViewState: overlayViewState, style: { width: "100%", height: "100%" }, mapStyle: layer.mapStyle, onMove: (e) => {
|
|
122
|
+
if (config.layers[0]) {
|
|
123
|
+
handleViewStateChange(config.layers[0].layerId, e.viewState);
|
|
124
|
+
}
|
|
125
|
+
}, children: [(0, jsx_runtime_1.jsx)(NavControl, { position: "top-right", showZoom: true, showCompass: true }), preparedLayers.map((l, index) => {
|
|
126
|
+
const beforeId = index > 0 ? preparedLayers[index - 1].id : undefined;
|
|
127
|
+
return (0, jsx_runtime_1.jsx)(layers_1.MapLayer, { layer: l, beforeId: beforeId }, l.id);
|
|
128
|
+
}), (0, jsx_runtime_1.jsx)(popup_1.default, { coordinates: coordsByPane["overlay"] })] })] }));
|
|
129
|
+
}
|
|
130
|
+
// Side-by-side or attribute mode: show multiple maps
|
|
131
|
+
const layout = (0, react_1.useMemo)(() => {
|
|
132
|
+
const count = config.layers.length;
|
|
133
|
+
if (count === 1)
|
|
134
|
+
return { cols: 1, rows: 1 };
|
|
135
|
+
if (count === 2)
|
|
136
|
+
return { cols: 2, rows: 1 };
|
|
137
|
+
if (count === 3)
|
|
138
|
+
return { cols: 2, rows: 2 };
|
|
139
|
+
return { cols: 2, rows: Math.ceil(count / 2) };
|
|
140
|
+
}, [config.layers.length]);
|
|
141
|
+
const renderMap = (compLayer, index) => {
|
|
142
|
+
const preparedLayer = preparedLayers[index];
|
|
143
|
+
const viewState = viewStates[compLayer.layerId] || initialViewState;
|
|
144
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "relative w-full h-full", children: [(0, jsx_runtime_1.jsxs)("div", { className: "absolute top-2 left-2 z-10 bg-black/70 text-white px-3 py-1 rounded-md text-sm font-medium", children: [compLayer.layer.title, compLayer.attributeLabel && ((0, jsx_runtime_1.jsxs)("span", { className: "ml-2 text-xs text-gray-300", children: ["(", compLayer.attributeLabel, ")"] }))] }), (0, jsx_runtime_1.jsxs)(maplibre_1.default, { id: `compare-${compLayer.layerId}`, initialViewState: viewState, style: { position: "absolute", inset: 0 }, mapStyle: layer.mapStyle, onMouseMove: onMouseMove(compLayer.layerId), onMove: (e) => handleViewStateChange(compLayer.layerId, e.viewState), children: [(0, jsx_runtime_1.jsx)(NavControl, { position: "top-right", showZoom: true, showCompass: true }), (0, jsx_runtime_1.jsx)(layers_1.MapLayer, { layer: preparedLayer }), (0, jsx_runtime_1.jsx)(popup_1.default, { coordinates: coordsByPane[compLayer.layerId] })] }, config.syncView ? `sync-${JSON.stringify(viewState)}` : compLayer.layerId)] }, compLayer.layerId));
|
|
145
|
+
};
|
|
146
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "w-full h-screen relative", children: [config.showStats && (0, jsx_runtime_1.jsx)(comparison_stats_1.default, {}), (0, jsx_runtime_1.jsx)("div", { style: {
|
|
147
|
+
display: "grid",
|
|
148
|
+
gridTemplateColumns: `repeat(${layout.cols}, 1fr)`,
|
|
149
|
+
gridTemplateRows: `repeat(${layout.rows}, ${100 / layout.rows}vh)`,
|
|
150
|
+
height: "100vh",
|
|
151
|
+
}, children: config.layers.map((compLayer, index) => renderMap(compLayer, index)) })] }));
|
|
152
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map Comparison Wrapper Component
|
|
3
|
+
*
|
|
4
|
+
* This component works within the existing LayersProvider context
|
|
5
|
+
* Use this when you already have MapProvider and LayersProvider set up
|
|
6
|
+
*/
|
|
7
|
+
export default function ComparisonWrapper(): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=comparison-wrapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comparison-wrapper.d.ts","sourceRoot":"","sources":["../../../src/components/map-comparison/comparison-wrapper.tsx"],"names":[],"mappings":"AAOA;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,iBAAiB,4CAcxC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.default = ComparisonWrapper;
|
|
8
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
9
|
+
const react_1 = require("react");
|
|
10
|
+
const comparison_panel_1 = __importDefault(require("./comparison-panel"));
|
|
11
|
+
const comparison_view_1 = __importDefault(require("./comparison-view"));
|
|
12
|
+
const use_map_comparison_1 = require("./use-map-comparison");
|
|
13
|
+
/**
|
|
14
|
+
* Map Comparison Wrapper Component
|
|
15
|
+
*
|
|
16
|
+
* This component works within the existing LayersProvider context
|
|
17
|
+
* Use this when you already have MapProvider and LayersProvider set up
|
|
18
|
+
*/
|
|
19
|
+
function ComparisonWrapper() {
|
|
20
|
+
const { setIsOpen } = (0, use_map_comparison_1.useMapComparison)();
|
|
21
|
+
// Auto-open the comparison panel when comparison mode is activated
|
|
22
|
+
(0, react_1.useEffect)(() => {
|
|
23
|
+
setIsOpen(true);
|
|
24
|
+
}, [setIsOpen]);
|
|
25
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "w-full h-screen relative", children: [(0, jsx_runtime_1.jsx)(comparison_panel_1.default, {}), (0, jsx_runtime_1.jsx)(comparison_view_1.default, {})] }));
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/map-comparison/index.tsx"],"names":[],"mappings":"AAsJA,QAAA,MAAM,aAAa,+CAoQlB,CAAC;AAEF,eAAe,aAAa,CAAC"}
|