@premiate/strapi-plugin-maplibre-field 1.0.6
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.
Potentially problematic release.
This version of @premiate/strapi-plugin-maplibre-field might be problematic. Click here for more details.
- package/CHANGELOG.md +77 -0
- package/LICENSE +23 -0
- package/README.md +781 -0
- package/dist/_chunks/de-CGU2cyif.mjs +16 -0
- package/dist/_chunks/de-Dq_t3Z6M.js +16 -0
- package/dist/_chunks/en-BxxNWf9i.mjs +16 -0
- package/dist/_chunks/en-CgSPA-1L.js +16 -0
- package/dist/_chunks/es-B_cPv3G5.mjs +16 -0
- package/dist/_chunks/es-Sgja1XAa.js +16 -0
- package/dist/_chunks/fr-B3JIzyzo.js +16 -0
- package/dist/_chunks/fr-Dw5wEoDC.mjs +16 -0
- package/dist/_chunks/index-BF5T-kqa.mjs +171 -0
- package/dist/_chunks/index-BNnkn7JG.mjs +1778 -0
- package/dist/_chunks/index-CGJogtZr.js +1799 -0
- package/dist/_chunks/index-nbk0hg-O.js +170 -0
- package/dist/_chunks/it-BgWDIXzn.js +16 -0
- package/dist/_chunks/it-CoUEVPt6.mjs +16 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/admin/src/components/Initializer.d.ts +6 -0
- package/dist/admin/src/components/MapInput/basemap-control.d.ts +8 -0
- package/dist/admin/src/components/MapInput/credits-control.d.ts +10 -0
- package/dist/admin/src/components/MapInput/geocoder-control.d.ts +20 -0
- package/dist/admin/src/components/MapInput/index.d.ts +19 -0
- package/dist/admin/src/components/MapInput/layer-control.d.ts +18 -0
- package/dist/admin/src/components/PluginIcon.d.ts +2 -0
- package/dist/admin/src/hooks/usePluginConfig.d.ts +2 -0
- package/dist/admin/src/index.d.ts +16 -0
- package/dist/admin/src/mutations/mutateEditViewHook.d.ts +30 -0
- package/dist/admin/src/services/poi-service.d.ts +160 -0
- package/dist/admin/src/utils/getTrad.d.ts +2 -0
- package/dist/admin/src/utils/pluginId.d.ts +2 -0
- package/dist/admin/src/utils/prefixPluginTranslations.d.ts +3 -0
- package/dist/server/index.js +107 -0
- package/dist/server/index.mjs +108 -0
- package/dist/server/src/bootstrap.d.ts +2 -0
- package/dist/server/src/config/index.d.ts +61 -0
- package/dist/server/src/config/schema.d.ts +58 -0
- package/dist/server/src/controllers/config.d.ts +7 -0
- package/dist/server/src/controllers/index.d.ts +8 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/index.d.ts +85 -0
- package/dist/server/src/register.d.ts +5 -0
- package/dist/server/src/routes/index.d.ts +9 -0
- package/dist/server/src/types/config.d.ts +26 -0
- package/logo.png +0 -0
- package/package.json +99 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const React = require("react");
|
|
3
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const icons = require("@strapi/icons");
|
|
5
|
+
const designSystem = require("@strapi/design-system");
|
|
6
|
+
const styledComponents = require("styled-components");
|
|
7
|
+
const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
|
|
8
|
+
const v = glob[path];
|
|
9
|
+
if (v) {
|
|
10
|
+
return typeof v === "function" ? v() : Promise.resolve(v);
|
|
11
|
+
}
|
|
12
|
+
return new Promise((_, reject) => {
|
|
13
|
+
(typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
|
|
14
|
+
reject.bind(
|
|
15
|
+
null,
|
|
16
|
+
new Error(
|
|
17
|
+
"Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
|
|
18
|
+
)
|
|
19
|
+
)
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
const name$1 = "@premiate/strapi-plugin-maplibre-field";
|
|
24
|
+
const strapi = {
|
|
25
|
+
name: "maplibre-field"
|
|
26
|
+
};
|
|
27
|
+
const pluginPkg = {
|
|
28
|
+
name: name$1,
|
|
29
|
+
strapi
|
|
30
|
+
};
|
|
31
|
+
const pluginId = pluginPkg.name.replace(/^(@[^/]+\/)?strapi-plugin-/i, "");
|
|
32
|
+
const Initializer = ({ setPlugin }) => {
|
|
33
|
+
const ref = React.useRef(false);
|
|
34
|
+
React.useEffect(() => {
|
|
35
|
+
if (!ref.current) {
|
|
36
|
+
setPlugin("maplibre-field");
|
|
37
|
+
ref.current = true;
|
|
38
|
+
}
|
|
39
|
+
}, [setPlugin]);
|
|
40
|
+
return null;
|
|
41
|
+
};
|
|
42
|
+
const IconBox = styledComponents.styled(designSystem.Flex)`
|
|
43
|
+
/* Hard code color values */
|
|
44
|
+
/* to stay consistent between themes */
|
|
45
|
+
background-color: #f0f0ff; /* primary100 */
|
|
46
|
+
border: 1px solid #d9d8ff; /* primary200 */
|
|
47
|
+
|
|
48
|
+
svg > path {
|
|
49
|
+
fill: #4945ff; /* primary600 */
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
const PluginIcon = () => {
|
|
53
|
+
return /* @__PURE__ */ jsxRuntime.jsx(IconBox, { justifyContent: "center", alignItems: "center", width: 7, height: 6, hasRadius: true, "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(icons.PinMap, {}) });
|
|
54
|
+
};
|
|
55
|
+
const mutateLayouts = (layouts) => {
|
|
56
|
+
return layouts.map((row) => {
|
|
57
|
+
return row.map((field) => {
|
|
58
|
+
const hasMapFieldEnabled = field.attribute?.pluginOptions?.["maplibre-field"]?.enabled;
|
|
59
|
+
if (!hasMapFieldEnabled) {
|
|
60
|
+
return field;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
...field,
|
|
64
|
+
attribute: {
|
|
65
|
+
...field.attribute,
|
|
66
|
+
customField: `plugin::${pluginId}.map`
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
const mutateEditViewHook = (editLayout) => {
|
|
73
|
+
if (!editLayout?.layout || !Array.isArray(editLayout.layout)) {
|
|
74
|
+
console.warn("maplibre-field: Invalid EditLayout structure received", editLayout);
|
|
75
|
+
return editLayout;
|
|
76
|
+
}
|
|
77
|
+
const mutatedLayout = mutateLayouts(editLayout.layout);
|
|
78
|
+
return {
|
|
79
|
+
...editLayout,
|
|
80
|
+
layout: mutatedLayout
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
const prefixPluginTranslations = (trad, pluginId2) => {
|
|
84
|
+
if (!pluginId2) {
|
|
85
|
+
throw new TypeError("pluginId can't be empty");
|
|
86
|
+
}
|
|
87
|
+
return Object.keys(trad).reduce((acc, current) => {
|
|
88
|
+
acc[`${pluginId2}.${current}`] = trad[current];
|
|
89
|
+
return acc;
|
|
90
|
+
}, {});
|
|
91
|
+
};
|
|
92
|
+
const name = pluginPkg.strapi.name;
|
|
93
|
+
const index = {
|
|
94
|
+
register(app) {
|
|
95
|
+
app.customFields.register({
|
|
96
|
+
name: "map",
|
|
97
|
+
pluginId,
|
|
98
|
+
type: "json",
|
|
99
|
+
intlLabel: {
|
|
100
|
+
id: "maplibre-field.label",
|
|
101
|
+
defaultMessage: "Map"
|
|
102
|
+
},
|
|
103
|
+
intlDescription: {
|
|
104
|
+
id: "maplibre-field.description",
|
|
105
|
+
defaultMessage: "A map custom field using MapLibre"
|
|
106
|
+
},
|
|
107
|
+
icon: PluginIcon,
|
|
108
|
+
components: {
|
|
109
|
+
Input: async () => Promise.resolve().then(() => require(
|
|
110
|
+
/* webpackChunkName: "input-component" */
|
|
111
|
+
"./index-CGJogtZr.js"
|
|
112
|
+
))
|
|
113
|
+
},
|
|
114
|
+
options: {
|
|
115
|
+
advanced: [
|
|
116
|
+
{
|
|
117
|
+
sectionTitle: null,
|
|
118
|
+
items: [
|
|
119
|
+
{
|
|
120
|
+
name: "options.pluginOptions.i18n.localized",
|
|
121
|
+
type: "checkbox",
|
|
122
|
+
intlLabel: {
|
|
123
|
+
id: "maplibre-field.i18n.localized.label",
|
|
124
|
+
defaultMessage: "Enable localization for this field"
|
|
125
|
+
},
|
|
126
|
+
description: {
|
|
127
|
+
id: "maplibre-field.i18n.localized.description",
|
|
128
|
+
defaultMessage: "The field can have different values in each language"
|
|
129
|
+
},
|
|
130
|
+
defaultValue: false
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
app.registerPlugin({
|
|
138
|
+
id: pluginId,
|
|
139
|
+
initializer: Initializer,
|
|
140
|
+
isReady: false,
|
|
141
|
+
name
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
bootstrap(app) {
|
|
145
|
+
app.registerHook(
|
|
146
|
+
"Admin/CM/pages/EditView/mutate-edit-view-layout",
|
|
147
|
+
mutateEditViewHook
|
|
148
|
+
);
|
|
149
|
+
},
|
|
150
|
+
async registerTrads({ locales }) {
|
|
151
|
+
const importedTrads = await Promise.all(
|
|
152
|
+
locales.map((locale) => {
|
|
153
|
+
return __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/de.json": () => Promise.resolve().then(() => require("./de-Dq_t3Z6M.js")), "./translations/en.json": () => Promise.resolve().then(() => require("./en-CgSPA-1L.js")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-Sgja1XAa.js")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-B3JIzyzo.js")), "./translations/it.json": () => Promise.resolve().then(() => require("./it-BgWDIXzn.js")) }), `./translations/${locale}.json`, 3).then(({ default: data }) => {
|
|
154
|
+
return {
|
|
155
|
+
data: prefixPluginTranslations(data, pluginId),
|
|
156
|
+
locale
|
|
157
|
+
};
|
|
158
|
+
}).catch(() => {
|
|
159
|
+
return {
|
|
160
|
+
data: {},
|
|
161
|
+
locale
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
})
|
|
165
|
+
);
|
|
166
|
+
return Promise.resolve(importedTrads);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
exports.index = index;
|
|
170
|
+
exports.pluginId = pluginId;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const it = {
|
|
4
|
+
"maplibre-field.label": "Mappa",
|
|
5
|
+
"maplibre-field.search": "Cerca un indirizzo o luogo su OpenStreetMap",
|
|
6
|
+
"maplibre-field.description": "Un custom field per mappe usando Maplibre",
|
|
7
|
+
"maplibre-field.fields.address": "indirizzo",
|
|
8
|
+
"maplibre-field.fields.longitude": "longitudine",
|
|
9
|
+
"maplibre-field.fields.latitude": "latitudine",
|
|
10
|
+
"maplibre-field.geocoding.success": "Indirizzo aggiornato con successo",
|
|
11
|
+
"maplibre-field.geocoding.error": "Impossibile recuperare l'indirizzo. Riprova.",
|
|
12
|
+
"maplibre-field.geocoding.no-results": "Nessun indirizzo trovato per questa posizione",
|
|
13
|
+
"maplibre-field.i18n.localized.label": "Abilita la localizzazione per questo campo",
|
|
14
|
+
"maplibre-field.i18n.localized.description": "Il campo può avere valori diversi in ogni lingua"
|
|
15
|
+
};
|
|
16
|
+
exports.default = it;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const it = {
|
|
2
|
+
"maplibre-field.label": "Mappa",
|
|
3
|
+
"maplibre-field.search": "Cerca un indirizzo o luogo su OpenStreetMap",
|
|
4
|
+
"maplibre-field.description": "Un custom field per mappe usando Maplibre",
|
|
5
|
+
"maplibre-field.fields.address": "indirizzo",
|
|
6
|
+
"maplibre-field.fields.longitude": "longitudine",
|
|
7
|
+
"maplibre-field.fields.latitude": "latitudine",
|
|
8
|
+
"maplibre-field.geocoding.success": "Indirizzo aggiornato con successo",
|
|
9
|
+
"maplibre-field.geocoding.error": "Impossibile recuperare l'indirizzo. Riprova.",
|
|
10
|
+
"maplibre-field.geocoding.no-results": "Nessun indirizzo trovato per questa posizione",
|
|
11
|
+
"maplibre-field.i18n.localized.label": "Abilita la localizzazione per questo campo",
|
|
12
|
+
"maplibre-field.i18n.localized.description": "Il campo può avere valori diversi in ogni lingua"
|
|
13
|
+
};
|
|
14
|
+
export {
|
|
15
|
+
it as default
|
|
16
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { MapStyle } from '../../../../server/src/types/config';
|
|
2
|
+
interface BasemapControlProps {
|
|
3
|
+
mapStyles: MapStyle[];
|
|
4
|
+
currentStyleUrl: string;
|
|
5
|
+
onStyleChange: (styleUrl: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export default function BasemapControlComponent({ mapStyles, currentStyleUrl, onStyleChange, }: BasemapControlProps): null;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { MapRef } from 'react-map-gl/maplibre';
|
|
3
|
+
interface CreditsControlProps {
|
|
4
|
+
mapRef: React.RefObject<MapRef>;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* React wrapper for MapLibre Credits Control
|
|
8
|
+
*/
|
|
9
|
+
declare const CreditsControl: React.FC<CreditsControlProps>;
|
|
10
|
+
export default CreditsControl;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { MapRef } from 'react-map-gl/maplibre';
|
|
3
|
+
import '@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css';
|
|
4
|
+
interface GeocoderResult {
|
|
5
|
+
result: {
|
|
6
|
+
center?: [number, number];
|
|
7
|
+
place_name: string;
|
|
8
|
+
geometry?: {
|
|
9
|
+
type: string;
|
|
10
|
+
coordinates: [number, number];
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
interface GeocoderControlProps {
|
|
15
|
+
mapRef: React.RefObject<MapRef>;
|
|
16
|
+
position?: string;
|
|
17
|
+
onResult?: (evt: GeocoderResult) => void;
|
|
18
|
+
}
|
|
19
|
+
declare const GeocoderControl: React.FC<GeocoderControlProps>;
|
|
20
|
+
export default GeocoderControl;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
3
|
+
interface MapFieldProps {
|
|
4
|
+
intlLabel: {
|
|
5
|
+
id: string;
|
|
6
|
+
defaultMessage: string;
|
|
7
|
+
};
|
|
8
|
+
name: string;
|
|
9
|
+
onChange: (event: {
|
|
10
|
+
target: {
|
|
11
|
+
name: string;
|
|
12
|
+
value: string;
|
|
13
|
+
type: string;
|
|
14
|
+
};
|
|
15
|
+
}) => void;
|
|
16
|
+
value: string | null;
|
|
17
|
+
}
|
|
18
|
+
declare const MapField: React.FC<MapFieldProps>;
|
|
19
|
+
export default MapField;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { MapRef } from 'react-map-gl/maplibre';
|
|
3
|
+
export interface LayerConfig {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
color?: string;
|
|
8
|
+
}
|
|
9
|
+
interface LayerControlProps {
|
|
10
|
+
mapRef: React.RefObject<MapRef>;
|
|
11
|
+
layers: LayerConfig[];
|
|
12
|
+
onLayerToggle: (layerId: string, enabled: boolean) => void;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* React wrapper for MapLibre Layer Control
|
|
16
|
+
*/
|
|
17
|
+
declare const LayerControl: React.FC<LayerControlProps>;
|
|
18
|
+
export default LayerControl;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
register(app: any): void;
|
|
3
|
+
bootstrap(app: any): void;
|
|
4
|
+
registerTrads({ locales }: {
|
|
5
|
+
locales: string[];
|
|
6
|
+
}): Promise<({
|
|
7
|
+
data: {
|
|
8
|
+
[x: string]: string;
|
|
9
|
+
};
|
|
10
|
+
locale: string;
|
|
11
|
+
} | {
|
|
12
|
+
data: {};
|
|
13
|
+
locale: string;
|
|
14
|
+
})[]>;
|
|
15
|
+
};
|
|
16
|
+
export default _default;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
interface EditFieldLayout {
|
|
2
|
+
attribute?: {
|
|
3
|
+
customField?: string;
|
|
4
|
+
pluginOptions?: {
|
|
5
|
+
[key: string]: {
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
type?: string;
|
|
10
|
+
};
|
|
11
|
+
name?: string;
|
|
12
|
+
size?: number;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
}
|
|
15
|
+
interface EditLayout {
|
|
16
|
+
layout: Array<Array<EditFieldLayout>>;
|
|
17
|
+
components: {
|
|
18
|
+
[uid: string]: {
|
|
19
|
+
layout: Array<EditFieldLayout>;
|
|
20
|
+
settings: any;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
metadatas: {
|
|
24
|
+
[key: string]: any;
|
|
25
|
+
};
|
|
26
|
+
options: any;
|
|
27
|
+
settings: any;
|
|
28
|
+
}
|
|
29
|
+
declare const mutateEditViewHook: (editLayout: EditLayout) => EditLayout;
|
|
30
|
+
export default mutateEditViewHook;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POI Service Layer
|
|
3
|
+
*
|
|
4
|
+
* Handles POI queries from multiple sources (Nominatim, Custom GeoJSON APIs)
|
|
5
|
+
* Provides distance calculations, viewport filtering, and data transformation
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Properties for a LocationFeature (all optional, omitted if not available)
|
|
9
|
+
*/
|
|
10
|
+
export interface LocationProperties {
|
|
11
|
+
/** POI name or short location name */
|
|
12
|
+
name?: string;
|
|
13
|
+
/** Full formatted address */
|
|
14
|
+
address?: string;
|
|
15
|
+
/** Source identifier: "nominatim" or custom source ID like "fotta-skatespots" */
|
|
16
|
+
source?: string;
|
|
17
|
+
/** Original ID from the source */
|
|
18
|
+
sourceId?: string;
|
|
19
|
+
/** Display name of the source layer, e.g., "Fotta Skatespots" */
|
|
20
|
+
sourceLayer?: string;
|
|
21
|
+
/** POI category/type: "skating_spot", "bus_stop", etc. */
|
|
22
|
+
category?: string;
|
|
23
|
+
/** How the location was created: "search" | "poi_click" | "map_click" */
|
|
24
|
+
inputMethod?: 'search' | 'poi_click' | 'map_click';
|
|
25
|
+
/** Original metadata preserved from the source */
|
|
26
|
+
metadata?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* GeoJSON Feature for storing location data (RFC 7946 compliant)
|
|
30
|
+
* This is the canonical format for storing locations in the database.
|
|
31
|
+
*/
|
|
32
|
+
export interface LocationFeature {
|
|
33
|
+
type: 'Feature';
|
|
34
|
+
geometry: {
|
|
35
|
+
type: 'Point';
|
|
36
|
+
coordinates: [number, number];
|
|
37
|
+
};
|
|
38
|
+
properties: LocationProperties;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create a LocationFeature with clean properties (no null/empty values)
|
|
42
|
+
* @param coordinates [lng, lat] coordinates
|
|
43
|
+
* @param properties Optional properties to include
|
|
44
|
+
* @returns LocationFeature with only defined, non-empty properties
|
|
45
|
+
*/
|
|
46
|
+
export declare function createLocationFeature(coordinates: [number, number], properties?: Partial<LocationProperties>): LocationFeature;
|
|
47
|
+
export interface POI {
|
|
48
|
+
id: string;
|
|
49
|
+
name: string;
|
|
50
|
+
type: string;
|
|
51
|
+
coordinates: [number, number];
|
|
52
|
+
address: string;
|
|
53
|
+
distance?: number;
|
|
54
|
+
metadata?: Record<string, any>;
|
|
55
|
+
source: 'nominatim' | 'custom';
|
|
56
|
+
mapName?: string;
|
|
57
|
+
layerId?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface GeoJSONFeature {
|
|
60
|
+
id?: string;
|
|
61
|
+
type: 'Feature';
|
|
62
|
+
geometry: {
|
|
63
|
+
type: 'Point';
|
|
64
|
+
coordinates: [number, number];
|
|
65
|
+
};
|
|
66
|
+
properties: {
|
|
67
|
+
name?: string | null;
|
|
68
|
+
[key: string]: any;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export interface GeoJSONFeatureCollection {
|
|
72
|
+
type: 'FeatureCollection';
|
|
73
|
+
features: GeoJSONFeature[];
|
|
74
|
+
}
|
|
75
|
+
export interface POIServiceConfig {
|
|
76
|
+
nominatimUrl: string;
|
|
77
|
+
customApiUrl?: string | null;
|
|
78
|
+
mapName?: string;
|
|
79
|
+
layerId?: string;
|
|
80
|
+
radius: number;
|
|
81
|
+
categories: string[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Calculate distance between two coordinates using Haversine formula
|
|
85
|
+
* @param coord1 First coordinate [lng, lat]
|
|
86
|
+
* @param coord2 Second coordinate [lng, lat]
|
|
87
|
+
* @returns Distance in meters
|
|
88
|
+
*/
|
|
89
|
+
export declare function calculateDistance(coord1: [number, number], coord2: [number, number]): number;
|
|
90
|
+
/**
|
|
91
|
+
* Query POIs from custom GeoJSON API
|
|
92
|
+
* @param apiUrl Custom API endpoint URL
|
|
93
|
+
* @param searchQuery Optional search query for filtering
|
|
94
|
+
* @returns Array of GeoJSON features
|
|
95
|
+
*/
|
|
96
|
+
export declare function queryCustomAPI(apiUrl: string, searchQuery?: string): Promise<GeoJSONFeature[]>;
|
|
97
|
+
/**
|
|
98
|
+
* Query POIs near coordinates from Nominatim
|
|
99
|
+
* Uses /reverse endpoint with high zoom level to get specific POIs
|
|
100
|
+
* @param lat Latitude
|
|
101
|
+
* @param lng Longitude
|
|
102
|
+
* @param radius Search radius in meters
|
|
103
|
+
* @param nominatimUrl Nominatim API base URL
|
|
104
|
+
* @returns Array of POIs
|
|
105
|
+
*/
|
|
106
|
+
export declare function queryNominatim(lat: number, lng: number, radius: number, nominatimUrl: string): Promise<POI[]>;
|
|
107
|
+
/**
|
|
108
|
+
* Convert GeoJSON feature to POI interface
|
|
109
|
+
* @param feature GeoJSON feature
|
|
110
|
+
* @param clickCoords Optional reference coordinates for distance calculation
|
|
111
|
+
* @param mapName Optional display name for the custom map source
|
|
112
|
+
* @param layerId Optional ID of the layer this POI belongs to
|
|
113
|
+
* @returns POI object or null if invalid
|
|
114
|
+
*/
|
|
115
|
+
export declare function geoJSONFeatureToPOI(feature: GeoJSONFeature, clickCoords?: [number, number], mapName?: string, layerId?: string): POI | null;
|
|
116
|
+
/**
|
|
117
|
+
* Filter GeoJSON features by distance from click point
|
|
118
|
+
* @param features Array of GeoJSON features
|
|
119
|
+
* @param clickCoords Click coordinates [lng, lat]
|
|
120
|
+
* @param radius Maximum distance in meters
|
|
121
|
+
* @returns Filtered features within radius
|
|
122
|
+
*/
|
|
123
|
+
export declare function filterByDistance(features: GeoJSONFeature[], clickCoords: [number, number], radius: number): GeoJSONFeature[];
|
|
124
|
+
/**
|
|
125
|
+
* Find nearest POI to click coordinates
|
|
126
|
+
* @param clickCoordinates Click coordinates [lng, lat]
|
|
127
|
+
* @param pois Array of POIs
|
|
128
|
+
* @returns Nearest POI or null if none found
|
|
129
|
+
*/
|
|
130
|
+
export declare function findNearestPOI(clickCoordinates: [number, number], pois: POI[]): POI | null;
|
|
131
|
+
/**
|
|
132
|
+
* Search POIs for geocoder (queries custom POI sources only)
|
|
133
|
+
* Note: Nominatim search is handled directly by geocoder-control.tsx
|
|
134
|
+
* @param query Search query string
|
|
135
|
+
* @param config POI service configuration
|
|
136
|
+
* @returns Array of custom POIs only
|
|
137
|
+
*/
|
|
138
|
+
export declare function searchPOIsForGeocoder(query: string, config: POIServiceConfig): Promise<POI[]>;
|
|
139
|
+
/**
|
|
140
|
+
* Search nearby POIs for snap (double-click) - queries BOTH sources
|
|
141
|
+
* @param lat Latitude
|
|
142
|
+
* @param lng Longitude
|
|
143
|
+
* @param config POI service configuration
|
|
144
|
+
* @returns Array of POIs from both Nominatim and Custom API
|
|
145
|
+
*/
|
|
146
|
+
export declare function searchNearbyPOIsForSnap(lat: number, lng: number, config: POIServiceConfig): Promise<POI[]>;
|
|
147
|
+
/**
|
|
148
|
+
* Query POIs for viewport display
|
|
149
|
+
* @param bounds Map bounds {north, south, east, west}
|
|
150
|
+
* @param center Map center [lng, lat]
|
|
151
|
+
* @param maxDisplay Maximum number of POIs to display
|
|
152
|
+
* @param config POI service configuration
|
|
153
|
+
* @returns Array of POIs sorted by distance from center
|
|
154
|
+
*/
|
|
155
|
+
export declare function queryPOIsForViewport(bounds: {
|
|
156
|
+
north: number;
|
|
157
|
+
south: number;
|
|
158
|
+
east: number;
|
|
159
|
+
west: number;
|
|
160
|
+
}, center: [number, number], maxDisplay: number, config: POIServiceConfig): Promise<POI[]>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const register = ({ strapi }) => {
|
|
3
|
+
strapi.customFields.register({
|
|
4
|
+
name: "map",
|
|
5
|
+
plugin: "maplibre-field",
|
|
6
|
+
type: "json"
|
|
7
|
+
// Icon will be loaded from admin side via intlLabel
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
const bootstrap = () => {
|
|
11
|
+
};
|
|
12
|
+
const destroy = ({ strapi }) => {
|
|
13
|
+
};
|
|
14
|
+
const schema = {
|
|
15
|
+
mapStyles: {
|
|
16
|
+
type: "array",
|
|
17
|
+
default: [
|
|
18
|
+
{
|
|
19
|
+
id: "satellite",
|
|
20
|
+
name: "Satellite",
|
|
21
|
+
url: "https://api.maptiler.com/maps/satellite-v4/style.json?key=sOr6q24xbu24UhKuyK8a",
|
|
22
|
+
isDefault: true
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "osm",
|
|
26
|
+
name: "OpenStreetMap",
|
|
27
|
+
url: "https://api.maptiler.com/maps/openstreetmap/style.json?key=sOr6q24xbu24UhKuyK8a"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
defaultZoom: {
|
|
32
|
+
type: "number",
|
|
33
|
+
default: 4.5
|
|
34
|
+
},
|
|
35
|
+
defaultCenter: {
|
|
36
|
+
type: "array",
|
|
37
|
+
default: [0, 0]
|
|
38
|
+
},
|
|
39
|
+
geocodingProvider: {
|
|
40
|
+
type: "string",
|
|
41
|
+
default: "nominatim"
|
|
42
|
+
},
|
|
43
|
+
nominatimUrl: {
|
|
44
|
+
type: "string",
|
|
45
|
+
default: "https://nominatim.openstreetmap.org"
|
|
46
|
+
},
|
|
47
|
+
poiDisplayEnabled: {
|
|
48
|
+
type: "boolean",
|
|
49
|
+
default: true,
|
|
50
|
+
description: "Display POI markers on map"
|
|
51
|
+
},
|
|
52
|
+
poiMinZoom: {
|
|
53
|
+
type: "number",
|
|
54
|
+
default: 10,
|
|
55
|
+
description: "Minimum zoom level to display POI markers"
|
|
56
|
+
},
|
|
57
|
+
poiMaxDisplay: {
|
|
58
|
+
type: "number",
|
|
59
|
+
default: 100,
|
|
60
|
+
description: "Maximum number of POIs to display on map"
|
|
61
|
+
},
|
|
62
|
+
poiSearchEnabled: {
|
|
63
|
+
type: "boolean",
|
|
64
|
+
default: true,
|
|
65
|
+
description: "Include custom API results in search field"
|
|
66
|
+
},
|
|
67
|
+
poiSnapRadius: {
|
|
68
|
+
type: "number",
|
|
69
|
+
default: 5,
|
|
70
|
+
description: "Snap radius in meters for double-click POI detection (default: 5m)"
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const config$1 = {
|
|
74
|
+
default: schema,
|
|
75
|
+
validator() {
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const config = ({ strapi }) => ({
|
|
79
|
+
getConfig(ctx) {
|
|
80
|
+
const config2 = strapi.config.get("plugin::maplibre-field");
|
|
81
|
+
console.log("[MapLibre Controller] Returning config:", JSON.stringify(config2, null, 2));
|
|
82
|
+
ctx.body = config2;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
const controllers = {
|
|
86
|
+
config
|
|
87
|
+
};
|
|
88
|
+
const routes = [
|
|
89
|
+
{
|
|
90
|
+
method: "GET",
|
|
91
|
+
path: "/config",
|
|
92
|
+
handler: "config.getConfig",
|
|
93
|
+
config: {
|
|
94
|
+
auth: false
|
|
95
|
+
// No authentication needed - config is public
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
];
|
|
99
|
+
const index = {
|
|
100
|
+
register,
|
|
101
|
+
bootstrap,
|
|
102
|
+
destroy,
|
|
103
|
+
config: config$1,
|
|
104
|
+
controllers,
|
|
105
|
+
routes
|
|
106
|
+
};
|
|
107
|
+
module.exports = index;
|