@dacostafilipe/react-geoportail 0.1.4 → 0.1.5

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.
@@ -1,6 +1,6 @@
1
1
  import { default as React } from 'react';
2
- import { LatLon, MapClickHandler, MarkerMode } from '../types/index.ts';
3
- import { LuxMapInstance } from '../types/lux.d.ts';
2
+ import { LatLon, MapClickHandler, MarkerMode } from '../types';
3
+ import { LuxMapInstance } from '../types/lux';
4
4
 
5
5
  export interface GeoportailMapProps {
6
6
  /**
@@ -1,4 +1,4 @@
1
- import { GeocodeQuery, LatLon } from '../types/index.ts';
1
+ import { GeocodeQuery, LatLon } from '../types';
2
2
 
3
3
  export interface GeocodeResultItem {
4
4
  latLon: LatLon;
@@ -1,4 +1,4 @@
1
- import { LuxNamespace } from '../types/lux.d.ts';
1
+ import { LuxNamespace } from '../types/lux';
2
2
 
3
3
  export type LuxApiState = {
4
4
  status: 'loading';
@@ -1,4 +1,4 @@
1
- import { LatLon, Address } from '../types/index.ts';
1
+ import { LatLon, Address } from '../types';
2
2
 
3
3
  export type ReverseGeocodeState = {
4
4
  status: 'idle';
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- export { GeoportailMap } from './components/GeoportailMap.tsx';
2
- export type { GeoportailMapProps, GeoportailMapHandle } from './components/GeoportailMap.tsx';
3
- export { useLuxApi } from './hooks/useLuxApi.ts';
4
- export type { LuxApiState } from './hooks/useLuxApi.ts';
5
- export { useReverseGeocode } from './hooks/useReverseGeocode.ts';
6
- export type { ReverseGeocodeState } from './hooks/useReverseGeocode.ts';
7
- export { useGeocode } from './hooks/useGeocode.ts';
8
- export type { GeocodeState, GeocodeResultItem } from './hooks/useGeocode.ts';
9
- export { latLonToLuref, lurefToLatLon } from './utils/coordinates.ts';
10
- export { loadLuxApi } from './utils/loader.ts';
11
- export type { LatLon, Address, GeocodeQuery, MapClickHandler, MarkerMode, } from './types/index.ts';
1
+ export { GeoportailMap } from './components/GeoportailMap';
2
+ export type { GeoportailMapProps, GeoportailMapHandle } from './components/GeoportailMap';
3
+ export { useLuxApi } from './hooks/useLuxApi';
4
+ export type { LuxApiState } from './hooks/useLuxApi';
5
+ export { useReverseGeocode } from './hooks/useReverseGeocode';
6
+ export type { ReverseGeocodeState } from './hooks/useReverseGeocode';
7
+ export { useGeocode } from './hooks/useGeocode';
8
+ export type { GeocodeState, GeocodeResultItem } from './hooks/useGeocode';
9
+ export { latLonToLuref, lurefToLatLon } from './utils/coordinates';
10
+ export { loadLuxApi } from './utils/loader';
11
+ export type { LatLon, Address, GeocodeQuery, MapClickHandler, MarkerMode, } from './types';
@@ -1,9 +1,9 @@
1
- import { jsx as T, jsxs as $ } from "react/jsx-runtime";
2
- import { useState as F, useEffect as S, forwardRef as nt, useId as st, useRef as y, useImperativeHandle as ot, useCallback as C } from "react";
3
- const B = "https://apiv3.geoportail.lu/apiv3loader.js", j = "geoportail-apiv3-loader";
4
- let R = null;
5
- function et() {
6
- return R || (R = new Promise((c, t) => {
1
+ import { jsx as C, jsxs as X } from "react/jsx-runtime";
2
+ import { useState as U, useEffect as S, forwardRef as tt, useRef as R, useImperativeHandle as nt, useCallback as T } from "react";
3
+ const B = "https://apiv3.geoportail.lu/apiv3loader.js", $ = "geoportail-apiv3-loader";
4
+ let y = null;
5
+ function st() {
6
+ return y || (y = new Promise((c, t) => {
7
7
  if (typeof window < "u" && window.lux) {
8
8
  c();
9
9
  return;
@@ -12,167 +12,167 @@ function et() {
12
12
  t(new Error("loadLuxApi must run in a browser environment"));
13
13
  return;
14
14
  }
15
- if (document.getElementById(j)) {
16
- V(c, t);
15
+ if (document.getElementById($)) {
16
+ j(c, t);
17
17
  return;
18
18
  }
19
19
  const n = document.createElement("script");
20
- n.id = j, n.src = B, n.async = !0, n.onload = () => V(c, t), n.onerror = () => {
21
- R = null, t(new Error(`Failed to load Geoportail API from ${B}`));
20
+ n.id = $, n.src = B, n.async = !0, n.onload = () => j(c, t), n.onerror = () => {
21
+ y = null, t(new Error(`Failed to load Geoportail API from ${B}`));
22
22
  }, document.head.appendChild(n);
23
- }), R);
23
+ }), y);
24
24
  }
25
- function V(c, t) {
26
- let i = 0;
25
+ function j(c, t) {
26
+ let u = 0;
27
27
  const a = () => {
28
28
  if (window.lux) {
29
29
  c();
30
30
  return;
31
31
  }
32
- if (i++ >= 100) {
33
- R = null, t(new Error("Timed out waiting for window.lux to be defined"));
32
+ if (u++ >= 100) {
33
+ y = null, t(new Error("Timed out waiting for window.lux to be defined"));
34
34
  return;
35
35
  }
36
36
  setTimeout(a, 50);
37
37
  };
38
38
  a();
39
39
  }
40
- function rt() {
41
- const [c, t] = F({
40
+ function ot() {
41
+ const [c, t] = U({
42
42
  status: "loading",
43
43
  lux: null,
44
44
  error: null
45
45
  });
46
46
  return S(() => {
47
47
  let n = !1;
48
- return et().then(() => {
48
+ return st().then(() => {
49
49
  n || t({ status: "ready", lux: window.lux, error: null });
50
- }).catch((i) => {
50
+ }).catch((u) => {
51
51
  n || t({
52
52
  status: "error",
53
53
  lux: null,
54
- error: i instanceof Error ? i : new Error(String(i))
54
+ error: u instanceof Error ? u : new Error(String(u))
55
55
  });
56
56
  }), () => {
57
57
  n = !0;
58
58
  };
59
59
  }, []), c;
60
60
  }
61
- const Z = 8e4, q = 1e5, H = 6.16666666666667, k = 49.8333333333333, D = 1, L = 6378388, z = 1 / 297, P = L * (1 - z), m = 1 - P * P / (L * L), _ = -87, G = -98, J = -121;
62
- function v(c) {
61
+ const Y = 8e4, Z = 1e5, q = 6.16666666666667, H = 49.8333333333333, I = 1, E = 6378388, F = 1 / 297, O = E * (1 - F), M = 1 - O * O / (E * E), D = -87, _ = -98, k = -121;
62
+ function x(c) {
63
63
  return c * Math.PI / 180;
64
64
  }
65
65
  function A(c) {
66
66
  return c * 180 / Math.PI;
67
67
  }
68
- function ct(c, t) {
69
- const n = v(c), i = v(t), a = Math.sin(n), s = Math.cos(n), p = Math.sin(i), g = Math.cos(i), o = 6378137, r = 1 / 298.257223563, h = o * (1 - r), f = 1 - h * h / (o * o), u = L - o, e = z - r, d = o / Math.sqrt(1 - f * a * a), w = 1 / (o * (1 - f) / Math.pow(1 - f * a * a, 1.5)) * (d * (f / (1 - f)) * a * s * (u / o) + (d / o + 1) * a * s * e * (o / h) - (_ * a * g + G * a * p - J * s)), x = 1 / (d * s) * (-_ * p + G * g);
68
+ function et(c, t) {
69
+ const n = x(c), u = x(t), a = Math.sin(n), s = Math.cos(n), p = Math.sin(u), g = Math.cos(u), o = 6378137, r = 1 / 298.257223563, f = o * (1 - r), l = 1 - f * f / (o * o), d = E - o, e = F - r, h = o / Math.sqrt(1 - l * a * a), w = 1 / (o * (1 - l) / Math.pow(1 - l * a * a, 1.5)) * (h * (l / (1 - l)) * a * s * (d / o) + (h / o + 1) * a * s * e * (o / f) - (D * a * g + _ * a * p - k * s)), m = 1 / (h * s) * (-D * p + _ * g);
70
70
  return {
71
71
  lat: c + A(w),
72
- lon: t + A(x)
72
+ lon: t + A(m)
73
73
  };
74
74
  }
75
- function at(c, t) {
76
- const n = v(c), i = v(t), a = Math.sin(n), s = Math.cos(n), p = Math.sin(i), g = Math.cos(i), o = L / Math.sqrt(1 - m * a * a), r = L * (1 - m) / Math.pow(1 - m * a * a, 1.5), h = 6378137 - L, u = 1 / 298.257223563 - z, e = 1 / r * (o * (m / (1 - m)) * a * s * (h / L) + (o / L + 1) * a * s * u * (L / P) + (_ * a * g + G * a * p - J * s)), d = 1 / (o * s) * (_ * p - G * g);
75
+ function rt(c, t) {
76
+ const n = x(c), u = x(t), a = Math.sin(n), s = Math.cos(n), p = Math.sin(u), g = Math.cos(u), o = E / Math.sqrt(1 - M * a * a), r = E * (1 - M) / Math.pow(1 - M * a * a, 1.5), f = 6378137 - E, d = 1 / 298.257223563 - F, e = 1 / r * (o * (M / (1 - M)) * a * s * (f / E) + (o / E + 1) * a * s * d * (E / O) + (D * a * g + _ * a * p - k * s)), h = 1 / (o * s) * (D * p - _ * g);
77
77
  return {
78
78
  lat: c + A(e),
79
- lon: t + A(d)
79
+ lon: t + A(h)
80
80
  };
81
81
  }
82
- function lt(c, t) {
83
- const n = v(c), i = v(t), a = v(H), s = v(k), p = Math.sin(n), g = Math.cos(n), o = Math.tan(n), r = L / Math.sqrt(1 - m * p * p), h = o * o, f = m / (1 - m) * g * g, u = i - a, e = g * u, d = U(n), M = U(s), w = Z + D * r * (e + e * e * e * (1 - h + f) / 6 + e * e * e * e * e * (5 - 18 * h + h * h + 72 * f) / 120), x = q + D * (d - M + r * o * (e * e / 2 + e * e * e * e * (5 - h + 9 * f + 4 * f * f) / 24 + e * e * e * e * e * e * (61 - 58 * h + h * h + 600 * f) / 720));
84
- return { easting: w, northing: x };
82
+ function ct(c, t) {
83
+ const n = x(c), u = x(t), a = x(q), s = x(H), p = Math.sin(n), g = Math.cos(n), o = Math.tan(n), r = E / Math.sqrt(1 - M * p * p), f = o * o, l = M / (1 - M) * g * g, d = u - a, e = g * d, h = P(n), L = P(s), w = Y + I * r * (e + e * e * e * (1 - f + l) / 6 + e * e * e * e * e * (5 - 18 * f + f * f + 72 * l) / 120), m = Z + I * (h - L + r * o * (e * e / 2 + e * e * e * e * (5 - f + 9 * l + 4 * l * l) / 24 + e * e * e * e * e * e * (61 - 58 * f + f * f + 600 * l) / 720));
84
+ return { easting: w, northing: m };
85
85
  }
86
- function it(c, t) {
87
- const n = c - Z, i = t - q, a = v(k), s = v(H), o = (U(a) + i / D) / (L * (1 - m / 4 - 3 * m * m / 64)), r = (1 - Math.sqrt(1 - m)) / (1 + Math.sqrt(1 - m)), h = o + (3 * r / 2 - 27 * r * r * r / 32) * Math.sin(2 * o) + (21 * r * r / 16 - 55 * r * r * r * r / 32) * Math.sin(4 * o) + 151 * r * r * r / 96 * Math.sin(6 * o), f = Math.sin(h), u = Math.cos(h), e = Math.tan(h), d = L / Math.sqrt(1 - m * f * f), M = e * e, w = m / (1 - m) * u * u, x = L * (1 - m) / Math.pow(1 - m * f * f, 1.5), l = n / (d * D), E = h - d * e / x * (l * l / 2 - l * l * l * l * (5 + 3 * M + 10 * w - 4 * w * w) / 24 + l * l * l * l * l * l * (61 + 90 * M + 298 * w + 45 * M * M) / 720), b = s + (l - l * l * l * (1 + 2 * M + w) / 6 + l * l * l * l * l * (5 - 2 * w + 28 * M - 3 * w * w) / 120) / u;
88
- return { lat: A(E), lon: A(b) };
86
+ function at(c, t) {
87
+ const n = c - Y, u = t - Z, a = x(H), s = x(q), o = (P(a) + u / I) / (E * (1 - M / 4 - 3 * M * M / 64)), r = (1 - Math.sqrt(1 - M)) / (1 + Math.sqrt(1 - M)), f = o + (3 * r / 2 - 27 * r * r * r / 32) * Math.sin(2 * o) + (21 * r * r / 16 - 55 * r * r * r * r / 32) * Math.sin(4 * o) + 151 * r * r * r / 96 * Math.sin(6 * o), l = Math.sin(f), d = Math.cos(f), e = Math.tan(f), h = E / Math.sqrt(1 - M * l * l), L = e * e, w = M / (1 - M) * d * d, m = E * (1 - M) / Math.pow(1 - M * l * l, 1.5), i = n / (h * I), v = f - h * e / m * (i * i / 2 - i * i * i * i * (5 + 3 * L + 10 * w - 4 * w * w) / 24 + i * i * i * i * i * i * (61 + 90 * L + 298 * w + 45 * L * L) / 720), b = s + (i - i * i * i * (1 + 2 * L + w) / 6 + i * i * i * i * i * (5 - 2 * w + 28 * L - 3 * w * w) / 120) / d;
88
+ return { lat: A(v), lon: A(b) };
89
89
  }
90
- function U(c) {
91
- const t = m, n = t * t, i = n * t;
92
- return L * ((1 - t / 4 - 3 * n / 64 - 5 * i / 256) * c - (3 * t / 8 + 3 * n / 32 + 45 * i / 1024) * Math.sin(2 * c) + (15 * n / 256 + 45 * i / 1024) * Math.sin(4 * c) - 35 * i / 3072 * Math.sin(6 * c));
90
+ function P(c) {
91
+ const t = M, n = t * t, u = n * t;
92
+ return E * ((1 - t / 4 - 3 * n / 64 - 5 * u / 256) * c - (3 * t / 8 + 3 * n / 32 + 45 * u / 1024) * Math.sin(2 * c) + (15 * n / 256 + 45 * u / 1024) * Math.sin(4 * c) - 35 * u / 3072 * Math.sin(6 * c));
93
93
  }
94
- function O(c, t) {
95
- const n = ct(c, t);
96
- return lt(n.lat, n.lon);
94
+ function G(c, t) {
95
+ const n = et(c, t);
96
+ return ct(n.lat, n.lon);
97
97
  }
98
- function K(c, t) {
99
- const n = it(c, t);
100
- return at(n.lat, n.lon);
98
+ function J(c, t) {
99
+ const n = at(c, t);
100
+ return rt(n.lat, n.lon);
101
101
  }
102
- const ut = { lat: 49.6116, lon: 6.1319 }, dt = "basemap_2015_global", Mt = nt(
102
+ const lt = { lat: 49.6116, lon: 6.1319 }, it = "basemap_2015_global", wt = tt(
103
103
  function({
104
- center: t = ut,
104
+ center: t = lt,
105
105
  zoom: n = 12,
106
- bgLayer: i = dt,
106
+ bgLayer: u = it,
107
107
  markerMode: a = "none",
108
108
  markerPosition: s,
109
109
  onMarkerPlace: p,
110
110
  className: g,
111
111
  style: o,
112
112
  layers: r
113
- }, h) {
114
- const u = `gp-map-${st().replace(/:/g, "")}`, e = rt(), d = y(null), M = y(null), w = y(null), x = y(p);
115
- return x.current = p, S(() => {
116
- if (e.status !== "ready") return;
117
- const l = e.lux, { easting: E, northing: b } = O(t.lat, t.lon), I = new l.Map({
118
- target: u,
119
- bgLayer: i,
113
+ }, f) {
114
+ const l = ot(), d = R(null), e = R(null), h = R(null), L = R(null), w = R(p);
115
+ return w.current = p, S(() => {
116
+ if (l.status !== "ready" || !d.current) return;
117
+ const m = l.lux, { easting: i, northing: v } = G(t.lat, t.lon), b = new m.Map({
118
+ target: d.current,
119
+ bgLayer: u,
120
120
  zoom: n,
121
- position: [E, b],
121
+ position: [i, v],
122
122
  ...r && r.length > 0 ? { layers: r, layerOpacities: r.map(() => 1) } : {}
123
123
  });
124
- return d.current = I, () => {
125
- d.current = null, M.current = null, w.current = null;
124
+ return e.current = b, () => {
125
+ e.current = null, h.current = null, L.current = null;
126
126
  };
127
- }, [e.status, u]), S(() => {
128
- const l = d.current;
129
- if (!l || e.status !== "ready") return;
130
- if (w.current && (l.un("singleclick", w.current), w.current = null), a === "none") {
131
- ft(l, M);
127
+ }, [l.status]), S(() => {
128
+ const m = e.current;
129
+ if (!m || l.status !== "ready") return;
130
+ if (L.current && (m.un("singleclick", L.current), L.current = null), a === "none") {
131
+ ut(m, h);
132
132
  return;
133
133
  }
134
- const E = s ?? (a === "fixed" ? t : void 0);
135
- if (E && Y(l, E, M), a === "click") {
136
- const b = (...I) => {
137
- var X;
138
- const N = I[0];
139
- if (!N.coordinate) return;
140
- const [Q, tt] = N.coordinate, W = K(Q, tt);
141
- Y(l, W, M), (X = x.current) == null || X.call(x, W);
134
+ const i = s ?? (a === "fixed" ? t : void 0);
135
+ if (i && V(m, i, h), a === "click") {
136
+ const v = (...b) => {
137
+ var W;
138
+ const z = b[0];
139
+ if (!z.coordinate) return;
140
+ const [K, Q] = z.coordinate, N = J(K, Q);
141
+ V(m, N, h), (W = w.current) == null || W.call(w, N);
142
142
  };
143
- w.current = b, l.on("singleclick", b);
143
+ L.current = v, m.on("singleclick", v);
144
144
  }
145
145
  }, [
146
- e.status,
146
+ l.status,
147
147
  a,
148
148
  s == null ? void 0 : s.lat,
149
149
  s == null ? void 0 : s.lon,
150
150
  t.lat,
151
151
  t.lon
152
- ]), ot(h, () => ({
153
- getLuxMap: () => d.current,
154
- setCenter(l) {
155
- const E = d.current;
156
- if (!E) return;
157
- const { easting: b, northing: I } = O(l.lat, l.lon);
158
- E.getView().setCenter([b, I]);
152
+ ]), nt(f, () => ({
153
+ getLuxMap: () => e.current,
154
+ setCenter(m) {
155
+ const i = e.current;
156
+ if (!i) return;
157
+ const { easting: v, northing: b } = G(m.lat, m.lon);
158
+ i.getView().setCenter([v, b]);
159
159
  },
160
- setZoom(l) {
161
- var E;
162
- (E = d.current) == null || E.getView().setZoom(l);
160
+ setZoom(m) {
161
+ var i;
162
+ (i = e.current) == null || i.getView().setZoom(m);
163
163
  }
164
- })), e.status === "error" ? /* @__PURE__ */ T(
164
+ })), l.status === "error" ? /* @__PURE__ */ C(
165
165
  "div",
166
166
  {
167
167
  className: g,
168
168
  style: { display: "flex", alignItems: "center", justifyContent: "center", background: "#f5f5f5", ...o },
169
- children: /* @__PURE__ */ $("span", { style: { color: "#c00" }, children: [
169
+ children: /* @__PURE__ */ X("span", { style: { color: "#c00" }, children: [
170
170
  "Failed to load Geoportail API: ",
171
- e.error.message
171
+ l.error.message
172
172
  ] })
173
173
  }
174
- ) : /* @__PURE__ */ $("div", { style: { position: "relative", ...o }, className: g, children: [
175
- e.status === "loading" && /* @__PURE__ */ T(
174
+ ) : /* @__PURE__ */ X("div", { style: { position: "relative", ...o }, className: g, children: [
175
+ l.status === "loading" && /* @__PURE__ */ C(
176
176
  "div",
177
177
  {
178
178
  style: {
@@ -184,75 +184,75 @@ const ut = { lat: 49.6116, lon: 6.1319 }, dt = "basemap_2015_global", Mt = nt(
184
184
  background: "rgba(255,255,255,0.7)",
185
185
  zIndex: 10
186
186
  },
187
- children: /* @__PURE__ */ T("span", { children: "Loading map…" })
187
+ children: /* @__PURE__ */ C("span", { children: "Loading map…" })
188
188
  }
189
189
  ),
190
- /* @__PURE__ */ T(
190
+ /* @__PURE__ */ C(
191
191
  "div",
192
192
  {
193
- id: u,
193
+ ref: d,
194
194
  style: { width: "100%", height: "100%" }
195
195
  }
196
196
  )
197
197
  ] });
198
198
  }
199
199
  );
200
- function Y(c, t, n) {
201
- const i = window.ol;
202
- if (!i) return;
203
- const { easting: a, northing: s } = O(t.lat, t.lon);
200
+ function V(c, t, n) {
201
+ const u = window.ol;
202
+ if (!u) return;
203
+ const { easting: a, northing: s } = G(t.lat, t.lon);
204
204
  if (n.current) {
205
205
  n.current.setPosition([a, s]);
206
206
  return;
207
207
  }
208
208
  const p = document.createElement("div");
209
- p.innerHTML = ht, p.style.cssText = "cursor:pointer;transform:translate(-50%,-100%);line-height:0;";
210
- const g = new i.Overlay({
209
+ p.innerHTML = dt, p.style.cssText = "cursor:pointer;transform:translate(-50%,-100%);line-height:0;";
210
+ const g = new u.Overlay({
211
211
  element: p,
212
212
  positioning: "bottom-center",
213
213
  stopEvent: !1
214
214
  });
215
215
  g.setPosition([a, s]), c.addOverlay(g), n.current = g;
216
216
  }
217
- function ft(c, t) {
217
+ function ut(c, t) {
218
218
  !t.current || !window.ol || (t.current.setPosition(void 0), t.current = null);
219
219
  }
220
- const ht = `<svg xmlns="http://www.w3.org/2000/svg" width="30" height="40" viewBox="0 0 30 40">
220
+ const dt = `<svg xmlns="http://www.w3.org/2000/svg" width="30" height="40" viewBox="0 0 30 40">
221
221
  <path d="M15 0C7.268 0 1 6.268 1 14c0 10.5 14 26 14 26S29 24.5 29 14C29 6.268 22.732 0 15 0z"
222
222
  fill="#e53935" stroke="#b71c1c" stroke-width="1.5"/>
223
223
  <circle cx="15" cy="14" r="5" fill="white"/>
224
- </svg>`, gt = "https://api.geoportail.lu/geocoder/reverseGeocode";
225
- function Lt() {
226
- const [c, t] = F({
224
+ </svg>`, ft = "https://api.geoportail.lu/geocoder/reverseGeocode";
225
+ function mt() {
226
+ const [c, t] = U({
227
227
  status: "idle",
228
228
  address: null,
229
229
  error: null
230
- }), n = y(null);
230
+ }), n = R(null);
231
231
  S(() => () => {
232
232
  var s;
233
233
  (s = n.current) == null || s.abort();
234
234
  }, []);
235
- const i = C(async (s) => {
235
+ const u = T(async (s) => {
236
236
  var g, o;
237
237
  (g = n.current) == null || g.abort();
238
238
  const p = new AbortController();
239
239
  n.current = p, t({ status: "loading", address: null, error: null });
240
240
  try {
241
- const { easting: r, northing: h } = O(s.lat, s.lon), f = new URL(gt);
242
- f.searchParams.set("easting", String(r)), f.searchParams.set("northing", String(h));
243
- const u = await fetch(f.toString(), { signal: p.signal });
244
- if (!u.ok)
245
- throw new Error(`Reverse geocode request failed: ${u.status} ${u.statusText}`);
246
- const d = (o = (await u.json()).results) == null ? void 0 : o[0];
247
- if (!d)
241
+ const { easting: r, northing: f } = G(s.lat, s.lon), l = new URL(ft);
242
+ l.searchParams.set("easting", String(r)), l.searchParams.set("northing", String(f));
243
+ const d = await fetch(l.toString(), { signal: p.signal });
244
+ if (!d.ok)
245
+ throw new Error(`Reverse geocode request failed: ${d.status} ${d.statusText}`);
246
+ const h = (o = (await d.json()).results) == null ? void 0 : o[0];
247
+ if (!h)
248
248
  throw new Error("No results returned for this position");
249
249
  t({
250
250
  status: "success",
251
251
  address: {
252
- label: d.name ?? "",
253
- distance: d.distance ?? 0,
254
- easting: d.easting,
255
- northing: d.northing
252
+ label: h.name ?? "",
253
+ distance: h.distance ?? 0,
254
+ easting: h.easting,
255
+ northing: h.northing
256
256
  },
257
257
  error: null
258
258
  });
@@ -264,48 +264,48 @@ function Lt() {
264
264
  error: r instanceof Error ? r : new Error(String(r))
265
265
  });
266
266
  }
267
- }, []), a = C(() => {
267
+ }, []), a = T(() => {
268
268
  var s;
269
269
  (s = n.current) == null || s.abort(), t({ status: "idle", address: null, error: null });
270
270
  }, []);
271
- return { state: c, lookup: i, reset: a };
271
+ return { state: c, lookup: u, reset: a };
272
272
  }
273
- const pt = "https://apiv3.geoportail.lu/geocode/search";
274
- function Et() {
275
- const [c, t] = F({
273
+ const ht = "https://apiv3.geoportail.lu/geocode/search";
274
+ function Mt() {
275
+ const [c, t] = U({
276
276
  status: "idle",
277
277
  results: null,
278
278
  error: null
279
- }), n = y(null);
279
+ }), n = R(null);
280
280
  S(() => () => {
281
281
  var s;
282
282
  (s = n.current) == null || s.abort();
283
283
  }, []);
284
- const i = C(async (s) => {
284
+ const u = T(async (s) => {
285
285
  var g;
286
286
  (g = n.current) == null || g.abort();
287
287
  const p = new AbortController();
288
288
  n.current = p, t({ status: "loading", results: null, error: null });
289
289
  try {
290
- const o = new URL(pt);
290
+ const o = new URL(ht);
291
291
  s.queryString ? o.searchParams.set("queryString", s.queryString) : (s.num && o.searchParams.set("num", s.num), s.street && o.searchParams.set("street", s.street), s.zip && o.searchParams.set("zip", s.zip), s.locality && o.searchParams.set("locality", s.locality));
292
292
  const r = await fetch(o.toString(), { signal: p.signal });
293
293
  if (!r.ok)
294
294
  throw new Error(`Geocode request failed: ${r.status} ${r.statusText}`);
295
- const f = ((await r.json()).results ?? []).map((u) => {
296
- var e, d, M, w;
295
+ const l = ((await r.json()).results ?? []).map((d) => {
296
+ var e, h, L, w;
297
297
  return {
298
- latLon: K(u.easting, u.northing),
299
- easting: u.easting,
300
- northing: u.northing,
301
- accuracy: u.accuracy ?? 0,
302
- street: (e = u.AddressDetails) == null ? void 0 : e.street,
303
- num: (d = u.AddressDetails) == null ? void 0 : d.number,
304
- zip: (M = u.AddressDetails) == null ? void 0 : M.zip,
305
- locality: (w = u.AddressDetails) == null ? void 0 : w.locality
298
+ latLon: J(d.easting, d.northing),
299
+ easting: d.easting,
300
+ northing: d.northing,
301
+ accuracy: d.accuracy ?? 0,
302
+ street: (e = d.AddressDetails) == null ? void 0 : e.street,
303
+ num: (h = d.AddressDetails) == null ? void 0 : h.number,
304
+ zip: (L = d.AddressDetails) == null ? void 0 : L.zip,
305
+ locality: (w = d.AddressDetails) == null ? void 0 : w.locality
306
306
  };
307
307
  });
308
- t({ status: "success", results: f, error: null });
308
+ t({ status: "success", results: l, error: null });
309
309
  } catch (o) {
310
310
  if (o instanceof DOMException && o.name === "AbortError") return;
311
311
  t({
@@ -314,19 +314,19 @@ function Et() {
314
314
  error: o instanceof Error ? o : new Error(String(o))
315
315
  });
316
316
  }
317
- }, []), a = C(() => {
317
+ }, []), a = T(() => {
318
318
  var s;
319
319
  (s = n.current) == null || s.abort(), t({ status: "idle", results: null, error: null });
320
320
  }, []);
321
- return { state: c, search: i, reset: a };
321
+ return { state: c, search: u, reset: a };
322
322
  }
323
323
  export {
324
- Mt as GeoportailMap,
325
- O as latLonToLuref,
326
- et as loadLuxApi,
327
- K as lurefToLatLon,
328
- Et as useGeocode,
329
- rt as useLuxApi,
330
- Lt as useReverseGeocode
324
+ wt as GeoportailMap,
325
+ G as latLonToLuref,
326
+ st as loadLuxApi,
327
+ J as lurefToLatLon,
328
+ Mt as useGeocode,
329
+ ot as useLuxApi,
330
+ mt as useReverseGeocode
331
331
  };
332
332
  //# sourceMappingURL=react-geoportail.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"react-geoportail.js","sources":["../src/utils/loader.ts","../src/hooks/useLuxApi.ts","../src/utils/coordinates.ts","../src/components/GeoportailMap.tsx","../src/hooks/useReverseGeocode.ts","../src/hooks/useGeocode.ts"],"sourcesContent":["/**\n * Dynamically injects the Geoportail v3 API script and resolves when\n * the global `lux` namespace is available.\n */\n\nconst LUX_SCRIPT_URL = 'https://apiv3.geoportail.lu/apiv3loader.js';\nconst LUX_SCRIPT_ID = 'geoportail-apiv3-loader';\n\nlet loadPromise: Promise<void> | null = null;\n\n/**\n * Loads the Geoportail apiv3loader.js script exactly once.\n * Safe to call multiple times — subsequent calls return the same promise.\n */\nexport function loadLuxApi(): Promise<void> {\n if (loadPromise) return loadPromise;\n\n loadPromise = new Promise<void>((resolve, reject) => {\n // Already loaded (e.g. added manually in HTML)\n if (typeof window !== 'undefined' && window.lux) {\n resolve();\n return;\n }\n\n if (typeof document === 'undefined') {\n reject(new Error('loadLuxApi must run in a browser environment'));\n return;\n }\n\n // Avoid duplicate script tags\n if (document.getElementById(LUX_SCRIPT_ID)) {\n pollForLux(resolve, reject);\n return;\n }\n\n const script = document.createElement('script');\n script.id = LUX_SCRIPT_ID;\n script.src = LUX_SCRIPT_URL;\n script.async = true;\n\n script.onload = () => pollForLux(resolve, reject);\n script.onerror = () => {\n loadPromise = null; // allow retry\n reject(new Error(`Failed to load Geoportail API from ${LUX_SCRIPT_URL}`));\n };\n\n document.head.appendChild(script);\n });\n\n return loadPromise;\n}\n\n/**\n * The lux API may initialise asynchronously after the script loads.\n * Poll until window.lux is defined (max ~5 s).\n */\nfunction pollForLux(resolve: () => void, reject: (err: Error) => void): void {\n const maxAttempts = 100;\n let attempts = 0;\n\n const check = () => {\n if (window.lux) {\n resolve();\n return;\n }\n if (attempts++ >= maxAttempts) {\n loadPromise = null;\n reject(new Error('Timed out waiting for window.lux to be defined'));\n return;\n }\n setTimeout(check, 50);\n };\n\n check();\n}\n","import { useState, useEffect } from 'react';\nimport { loadLuxApi } from '../utils/loader.ts';\nimport type { LuxNamespace } from '../types/lux.d.ts';\n\nexport type LuxApiState =\n | { status: 'loading'; lux: null; error: null }\n | { status: 'ready'; lux: LuxNamespace; error: null }\n | { status: 'error'; lux: null; error: Error };\n\n/**\n * Loads the Geoportail apiv3 script and returns the `lux` namespace once ready.\n * Deduplicates the script load — safe to call from multiple components.\n */\nexport function useLuxApi(): LuxApiState {\n const [state, setState] = useState<LuxApiState>({\n status: 'loading',\n lux: null,\n error: null,\n });\n\n useEffect(() => {\n let cancelled = false;\n\n loadLuxApi()\n .then(() => {\n if (!cancelled) {\n setState({ status: 'ready', lux: window.lux!, error: null });\n }\n })\n .catch((err: unknown) => {\n if (!cancelled) {\n setState({\n status: 'error',\n lux: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, []);\n\n return state;\n}\n","/**\n * Coordinate conversion between EPSG:2169 (Luxembourg TM) and EPSG:4326 (WGS84).\n *\n * The Geoportail API operates internally in EPSG:2169 but also accepts\n * position arrays in EPSG:4326 via `lux.Map` constructor options.\n *\n * For precise server-side conversion the REST API itself is authoritative.\n * The formulas below use a 6-parameter Helmert + transverse Mercator approach\n * sufficient for Luxembourg's territory (~50 km radius).\n *\n * Reference: IGN-L / ACT official parameters for LUREF (EPSG:2169).\n */\n\n// LUREF / Luxembourg TM projection constants (EPSG:2169)\nconst FE = 80000; // False Easting (m)\nconst FN = 100000; // False Northing (m)\nconst LON0 = 6.16666666666667; // Central meridian (6°10') in degrees\nconst LAT0 = 49.8333333333333; // Latitude of origin (49°50') in degrees\nconst SCALE = 1.0; // Scale factor at central meridian\nconst A = 6378388.0; // Semi-major axis (Hayford / International 1924)\nconst F = 1 / 297.0; // Flattening\nconst B = A * (1 - F); // Semi-minor axis\n\nconst E2 = 1 - (B * B) / (A * A); // First eccentricity squared\n\n// Helmert 7-parameter shift: ETRS89 -> ED50 (approximate for Luxembourg)\n// These values transform WGS84 (≈ETRS89) to ED50 used by LUREF\nconst DX = -87.0;\nconst DY = -98.0;\nconst DZ = -121.0;\n\nfunction degToRad(d: number): number {\n return (d * Math.PI) / 180;\n}\n\nfunction radToDeg(r: number): number {\n return (r * 180) / Math.PI;\n}\n\n/**\n * Molodensky transformation: WGS84 -> ED50 (approximate, <1 m accuracy).\n */\nfunction wgs84ToEd50(\n lat: number,\n lon: number\n): { lat: number; lon: number } {\n const latR = degToRad(lat);\n const lonR = degToRad(lon);\n\n const sinLat = Math.sin(latR);\n const cosLat = Math.cos(latR);\n const sinLon = Math.sin(lonR);\n const cosLon = Math.cos(lonR);\n\n const aWgs = 6378137.0;\n const fWgs = 1 / 298.257223563;\n const bWgs = aWgs * (1 - fWgs);\n const e2Wgs = 1 - (bWgs * bWgs) / (aWgs * aWgs);\n\n const da = A - aWgs;\n const df = F - fWgs;\n\n const N = aWgs / Math.sqrt(1 - e2Wgs * sinLat * sinLat);\n const M =\n (aWgs * (1 - e2Wgs)) /\n Math.pow(1 - e2Wgs * sinLat * sinLat, 1.5);\n\n const dLat =\n (1 / M) *\n (N * (e2Wgs / (1 - e2Wgs)) * sinLat * cosLat * (da / aWgs) +\n (N / aWgs + 1) * sinLat * cosLat * df * (aWgs / bWgs) -\n (DX * sinLat * cosLon + DY * sinLat * sinLon - DZ * cosLat));\n\n const dLon =\n (1 / (N * cosLat)) * (-DX * sinLon + DY * cosLon);\n\n return {\n lat: lat + radToDeg(dLat),\n lon: lon + radToDeg(dLon),\n };\n}\n\n/**\n * ED50 -> WGS84 (inverse Molodensky, approximate).\n */\nfunction ed50ToWgs84(\n lat: number,\n lon: number\n): { lat: number; lon: number } {\n const latR = degToRad(lat);\n const lonR = degToRad(lon);\n\n const sinLat = Math.sin(latR);\n const cosLat = Math.cos(latR);\n const sinLon = Math.sin(lonR);\n const cosLon = Math.cos(lonR);\n\n const N = A / Math.sqrt(1 - E2 * sinLat * sinLat);\n const M = (A * (1 - E2)) / Math.pow(1 - E2 * sinLat * sinLat, 1.5);\n\n const da = 6378137.0 - A;\n const fWgs = 1 / 298.257223563;\n const df = fWgs - F;\n\n const dLat =\n (1 / M) *\n (N * (E2 / (1 - E2)) * sinLat * cosLat * (da / A) +\n (N / A + 1) * sinLat * cosLat * df * (A / B) +\n (DX * sinLat * cosLon + DY * sinLat * sinLon - DZ * cosLat));\n\n const dLon =\n (1 / (N * cosLat)) * (DX * sinLon - DY * cosLon);\n\n return {\n lat: lat + radToDeg(dLat),\n lon: lon + radToDeg(dLon),\n };\n}\n\n/**\n * Transverse Mercator forward projection (ED50 lat/lon -> LUREF easting/northing).\n */\nfunction tmForward(lat: number, lon: number): { easting: number; northing: number } {\n const latR = degToRad(lat);\n const lonR = degToRad(lon);\n const lon0R = degToRad(LON0);\n const lat0R = degToRad(LAT0);\n\n const sinLat = Math.sin(latR);\n const cosLat = Math.cos(latR);\n const tanLat = Math.tan(latR);\n\n const N = A / Math.sqrt(1 - E2 * sinLat * sinLat);\n const T = tanLat * tanLat;\n const C = (E2 / (1 - E2)) * cosLat * cosLat;\n const dl = lonR - lon0R;\n const XX = cosLat * dl;\n\n // Meridian arc from equator to lat\n const M = meridianArc(latR);\n const M0 = meridianArc(lat0R);\n\n const easting =\n FE +\n SCALE *\n N *\n (XX +\n (XX * XX * XX * (1 - T + C)) / 6 +\n (XX * XX * XX * XX * XX * (5 - 18 * T + T * T + 72 * C)) / 120);\n\n const northing =\n FN +\n SCALE *\n (M -\n M0 +\n N *\n tanLat *\n (XX * XX / 2 +\n (XX * XX * XX * XX * (5 - T + 9 * C + 4 * C * C)) / 24 +\n (XX * XX * XX * XX * XX * XX *\n (61 - 58 * T + T * T + 600 * C)) /\n 720));\n\n return { easting, northing };\n}\n\n/**\n * Transverse Mercator inverse projection (LUREF easting/northing -> ED50 lat/lon).\n */\nfunction tmInverse(easting: number, northing: number): { lat: number; lon: number } {\n const x = easting - FE;\n const y = northing - FN;\n const lat0R = degToRad(LAT0);\n const lon0R = degToRad(LON0);\n const M0 = meridianArc(lat0R);\n\n // Footpoint latitude\n const M1 = M0 + y / SCALE;\n const mu = M1 / (A * (1 - E2 / 4 - (3 * E2 * E2) / 64));\n\n const e1 = (1 - Math.sqrt(1 - E2)) / (1 + Math.sqrt(1 - E2));\n const phi1 =\n mu +\n ((3 * e1) / 2 - (27 * e1 * e1 * e1) / 32) * Math.sin(2 * mu) +\n ((21 * e1 * e1) / 16 - (55 * e1 * e1 * e1 * e1) / 32) *\n Math.sin(4 * mu) +\n ((151 * e1 * e1 * e1) / 96) * Math.sin(6 * mu);\n\n const sinPhi1 = Math.sin(phi1);\n const cosPhi1 = Math.cos(phi1);\n const tanPhi1 = Math.tan(phi1);\n\n const N1 = A / Math.sqrt(1 - E2 * sinPhi1 * sinPhi1);\n const T1 = tanPhi1 * tanPhi1;\n const C1 = (E2 / (1 - E2)) * cosPhi1 * cosPhi1;\n const R1 =\n (A * (1 - E2)) / Math.pow(1 - E2 * sinPhi1 * sinPhi1, 1.5);\n const D = x / (N1 * SCALE);\n\n const lat =\n phi1 -\n ((N1 * tanPhi1) / R1) *\n (D * D / 2 -\n (D * D * D * D * (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1)) / 24 +\n (D * D * D * D * D * D *\n (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1)) /\n 720);\n\n const lon =\n lon0R +\n (D -\n (D * D * D * (1 + 2 * T1 + C1)) / 6 +\n (D * D * D * D * D * (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1)) / 120) /\n cosPhi1;\n\n return { lat: radToDeg(lat), lon: radToDeg(lon) };\n}\n\nfunction meridianArc(latR: number): number {\n const e2 = E2;\n const e4 = e2 * e2;\n const e6 = e4 * e2;\n return (\n A *\n ((1 - e2 / 4 - (3 * e4) / 64 - (5 * e6) / 256) * latR -\n ((3 * e2) / 8 + (3 * e4) / 32 + (45 * e6) / 1024) *\n Math.sin(2 * latR) +\n ((15 * e4) / 256 + (45 * e6) / 1024) * Math.sin(4 * latR) -\n ((35 * e6) / 3072) * Math.sin(6 * latR))\n );\n}\n\n/**\n * Convert WGS84 (lat/lon) to EPSG:2169 (easting/northing).\n */\nexport function latLonToLuref(\n lat: number,\n lon: number\n): { easting: number; northing: number } {\n const ed50 = wgs84ToEd50(lat, lon);\n return tmForward(ed50.lat, ed50.lon);\n}\n\n/**\n * Convert EPSG:2169 (easting/northing) to WGS84 (lat/lon).\n */\nexport function lurefToLatLon(\n easting: number,\n northing: number\n): { lat: number; lon: number } {\n const ed50 = tmInverse(easting, northing);\n return ed50ToWgs84(ed50.lat, ed50.lon);\n}\n","import React, {\n useEffect,\n useRef,\n useId,\n forwardRef,\n useImperativeHandle,\n} from 'react';\nimport { useLuxApi } from '../hooks/useLuxApi.ts';\nimport { lurefToLatLon, latLonToLuref } from '../utils/coordinates.ts';\nimport type { LatLon, MapClickHandler, MarkerMode } from '../types/index.ts';\nimport type { LuxMapInstance } from '../types/lux.d.ts';\n\nexport interface GeoportailMapProps {\n /**\n * Initial center of the map.\n * Defaults to Luxembourg City (lat: 49.6116, lon: 6.1319).\n */\n center?: LatLon;\n\n /** Initial zoom level (1–20). Default: 12 */\n zoom?: number;\n\n /**\n * Background layer identifier.\n * Default: 'basemap_2015_global'\n */\n bgLayer?: string;\n\n /**\n * Controls pin/marker behaviour:\n * - 'none' — no marker\n * - 'fixed' — show a marker at `markerPosition` (does not move on click)\n * - 'click' — user clicks map to place/move the pin; fires `onMarkerPlace`\n *\n * Default: 'none'\n */\n markerMode?: MarkerMode;\n\n /**\n * Position of the marker when `markerMode` is 'fixed' or to pre-set an\n * initial pin when `markerMode` is 'click'.\n */\n markerPosition?: LatLon;\n\n /**\n * Called whenever the user places a pin (markerMode === 'click').\n * Receives the WGS84 lat/lon of the clicked point.\n */\n onMarkerPlace?: MapClickHandler;\n\n /** CSS class applied to the map container div */\n className?: string;\n\n /** Inline styles for the map container div */\n style?: React.CSSProperties;\n\n /** Additional numeric layer IDs to add on top of the background */\n layers?: number[];\n}\n\nexport interface GeoportailMapHandle {\n /** Returns the underlying lux.Map instance (or null before ready) */\n getLuxMap(): LuxMapInstance | null;\n /** Programmatically move the map center */\n setCenter(coords: LatLon): void;\n /** Programmatically set zoom */\n setZoom(zoom: number): void;\n}\n\nconst LUXEMBOURG_CITY: LatLon = { lat: 49.6116, lon: 6.1319 };\nconst DEFAULT_BG_LAYER = 'basemap_2015_global';\n\n/**\n * Renders a Geoportail Luxembourg map inside a React component.\n *\n * Expose a ref (`GeoportailMapHandle`) to access the underlying lux.Map instance\n * or imperatively control the view.\n */\nexport const GeoportailMap = forwardRef<GeoportailMapHandle, GeoportailMapProps>(\n function GeoportailMap(\n {\n center = LUXEMBOURG_CITY,\n zoom = 12,\n bgLayer = DEFAULT_BG_LAYER,\n markerMode = 'none',\n markerPosition,\n onMarkerPlace,\n className,\n style,\n layers,\n },\n ref\n ) {\n const generatedId = useId();\n // useId produces \":r0:\" style strings — strip colons for valid DOM id\n const mapId = `gp-map-${generatedId.replace(/:/g, '')}`;\n\n const luxApi = useLuxApi();\n const mapRef = useRef<LuxMapInstance | null>(null);\n const markerLayerRef = useRef<unknown>(null);\n const clickListenerRef = useRef<((...args: unknown[]) => void) | null>(null);\n\n // Keep stable refs to callbacks so effects don't re-run on every render\n const onMarkerPlaceRef = useRef(onMarkerPlace);\n onMarkerPlaceRef.current = onMarkerPlace;\n\n // ------------------------------------------------------------------ map init\n useEffect(() => {\n if (luxApi.status !== 'ready') return;\n\n const lux = luxApi.lux;\n const { easting, northing } = latLonToLuref(center.lat, center.lon);\n\n const mapInstance = new lux.Map({\n target: mapId,\n bgLayer,\n zoom,\n position: [easting, northing],\n ...(layers && layers.length > 0\n ? { layers, layerOpacities: layers.map(() => 1) }\n : {}),\n });\n\n mapRef.current = mapInstance;\n\n return () => {\n // lux.Map does not expose a destroy(); we clear our references\n mapRef.current = null;\n markerLayerRef.current = null;\n clickListenerRef.current = null;\n };\n // Only re-run when the API becomes ready or the map target changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [luxApi.status, mapId]);\n\n // ------------------------------------------------------------------ marker\n useEffect(() => {\n const map = mapRef.current;\n if (!map || luxApi.status !== 'ready') return;\n\n // Remove previous click listener\n if (clickListenerRef.current) {\n map.un('singleclick', clickListenerRef.current);\n clickListenerRef.current = null;\n }\n\n if (markerMode === 'none') {\n clearMarker(map, markerLayerRef);\n return;\n }\n\n const initialPos = markerPosition ?? (markerMode === 'fixed' ? center : undefined);\n if (initialPos) {\n placeMarker(map, initialPos, markerLayerRef);\n }\n\n if (markerMode === 'click') {\n const handler = (...args: unknown[]) => {\n // OpenLayers MapBrowserEvent — coordinate is in map projection (EPSG:2169)\n const evt = args[0] as { coordinate?: [number, number] };\n if (!evt.coordinate) return;\n\n const [e, n] = evt.coordinate;\n const latLon = lurefToLatLon(e, n);\n placeMarker(map, latLon, markerLayerRef);\n onMarkerPlaceRef.current?.(latLon);\n };\n\n clickListenerRef.current = handler;\n map.on('singleclick', handler);\n }\n }, [\n luxApi.status,\n markerMode,\n markerPosition?.lat,\n markerPosition?.lon,\n center.lat,\n center.lon,\n ]);\n\n // ------------------------------------------------------------------ imperative handle\n useImperativeHandle(ref, () => ({\n getLuxMap: () => mapRef.current,\n setCenter(coords: LatLon) {\n const map = mapRef.current;\n if (!map) return;\n const { easting, northing } = latLonToLuref(coords.lat, coords.lon);\n map.getView().setCenter([easting, northing]);\n },\n setZoom(z: number) {\n mapRef.current?.getView().setZoom(z);\n },\n }));\n\n // ------------------------------------------------------------------ render\n if (luxApi.status === 'error') {\n return (\n <div\n className={className}\n style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#f5f5f5', ...style }}\n >\n <span style={{ color: '#c00' }}>\n Failed to load Geoportail API: {luxApi.error.message}\n </span>\n </div>\n );\n }\n\n return (\n <div style={{ position: 'relative', ...style }} className={className}>\n {luxApi.status === 'loading' && (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'rgba(255,255,255,0.7)',\n zIndex: 10,\n }}\n >\n <span>Loading map…</span>\n </div>\n )}\n <div\n id={mapId}\n style={{ width: '100%', height: '100%' }}\n />\n </div>\n );\n }\n);\n\n// ------------------------------------------------------------------ helpers\n\n/**\n * Place/move an SVG pin marker on the map.\n * lux.Map is built on OpenLayers 3 — we use ol.Overlay for the marker.\n */\nfunction placeMarker(\n map: LuxMapInstance,\n position: LatLon,\n layerRef: React.MutableRefObject<unknown>\n): void {\n // Prefer ol (OpenLayers global) if available\n const ol = (window as unknown as { ol?: OlLike }).ol;\n if (!ol) return;\n\n const { easting, northing } = latLonToLuref(position.lat, position.lon);\n\n // Reuse existing overlay or create a new one\n if (layerRef.current) {\n const overlay = layerRef.current as OlOverlay;\n overlay.setPosition([easting, northing]);\n return;\n }\n\n const el = document.createElement('div');\n el.innerHTML = PIN_SVG;\n el.style.cssText =\n 'cursor:pointer;transform:translate(-50%,-100%);line-height:0;';\n\n const overlay = new ol.Overlay({\n element: el,\n positioning: 'bottom-center',\n stopEvent: false,\n });\n\n overlay.setPosition([easting, northing]);\n (map as unknown as { addOverlay(o: OlOverlay): void }).addOverlay(overlay);\n layerRef.current = overlay;\n}\n\nfunction clearMarker(\n _map: LuxMapInstance,\n layerRef: React.MutableRefObject<unknown>\n): void {\n if (!layerRef.current) return;\n const ol = (window as unknown as { ol?: OlLike }).ol;\n if (!ol) return;\n // Overlay doesn't have a built-in remove on the overlay itself;\n // set position to undefined to hide it\n (layerRef.current as OlOverlay).setPosition(undefined);\n layerRef.current = null;\n}\n\n// Minimal pin SVG (red teardrop, 30×40)\nconst PIN_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"30\" height=\"40\" viewBox=\"0 0 30 40\">\n <path d=\"M15 0C7.268 0 1 6.268 1 14c0 10.5 14 26 14 26S29 24.5 29 14C29 6.268 22.732 0 15 0z\"\n fill=\"#e53935\" stroke=\"#b71c1c\" stroke-width=\"1.5\"/>\n <circle cx=\"15\" cy=\"14\" r=\"5\" fill=\"white\"/>\n</svg>`;\n\n// Minimal OL type stubs used only within this file\ninterface OlOverlay {\n setPosition(pos: [number, number] | undefined): void;\n}\n\ninterface OlLike {\n Overlay: new (opts: { element: HTMLElement; positioning: string; stopEvent: boolean }) => OlOverlay;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { latLonToLuref } from '../utils/coordinates.ts';\nimport type { LatLon, Address } from '../types/index.ts';\n\nconst REVERSE_GEOCODE_URL = 'https://api.geoportail.lu/geocoder/reverseGeocode';\n\nexport type ReverseGeocodeState =\n | { status: 'idle'; address: null; error: null }\n | { status: 'loading'; address: null; error: null }\n | { status: 'success'; address: Address; error: null }\n | { status: 'error'; address: null; error: Error };\n\n/**\n * Hook for reverse geocoding: convert a WGS84 lat/lon to a Luxembourg address.\n *\n * Uses the Geoportail REST reverse geocode endpoint — no API key required.\n *\n * @example\n * const { state, lookup } = useReverseGeocode();\n * // ...\n * lookup({ lat: 49.6116, lon: 6.1319 });\n * if (state.status === 'success') console.log(state.address.label);\n */\nexport function useReverseGeocode() {\n const [state, setState] = useState<ReverseGeocodeState>({\n status: 'idle',\n address: null,\n error: null,\n });\n\n const abortRef = useRef<AbortController | null>(null);\n\n useEffect(() => () => { abortRef.current?.abort(); }, []);\n\n const lookup = useCallback(async (position: LatLon) => {\n // Cancel any in-flight request\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setState({ status: 'loading', address: null, error: null });\n\n try {\n const { easting, northing } = latLonToLuref(position.lat, position.lon);\n\n const url = new URL(REVERSE_GEOCODE_URL);\n url.searchParams.set('easting', String(easting));\n url.searchParams.set('northing', String(northing));\n\n const res = await fetch(url.toString(), { signal: controller.signal });\n\n if (!res.ok) {\n throw new Error(`Reverse geocode request failed: ${res.status} ${res.statusText}`);\n }\n\n const data = (await res.json()) as {\n results?: Array<{\n easting: number;\n northing: number;\n name?: string;\n distance?: number;\n }>;\n };\n\n const first = data.results?.[0];\n if (!first) {\n throw new Error('No results returned for this position');\n }\n\n setState({\n status: 'success',\n address: {\n label: first.name ?? '',\n distance: first.distance ?? 0,\n easting: first.easting,\n northing: first.northing,\n },\n error: null,\n });\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n setState({\n status: 'error',\n address: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState({ status: 'idle', address: null, error: null });\n }, []);\n\n return { state, lookup, reset };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { lurefToLatLon } from '../utils/coordinates.ts';\nimport type { GeocodeQuery, LatLon } from '../types/index.ts';\n\nconst GEOCODE_URL = 'https://apiv3.geoportail.lu/geocode/search';\n\nexport interface GeocodeResultItem {\n latLon: LatLon;\n easting: number;\n northing: number;\n accuracy: number;\n street?: string;\n num?: string;\n zip?: string;\n locality?: string;\n}\n\nexport type GeocodeState =\n | { status: 'idle'; results: null; error: null }\n | { status: 'loading'; results: null; error: null }\n | { status: 'success'; results: GeocodeResultItem[]; error: null }\n | { status: 'error'; results: null; error: Error };\n\n/**\n * Hook for forward geocoding: search for a Luxembourg address and get coordinates.\n *\n * @example\n * const { state, search } = useGeocode();\n * search({ queryString: '1 rue du Fort Thüngen, Luxembourg' });\n * if (state.status === 'success') {\n * const { lat, lon } = state.results[0].latLon;\n * }\n */\nexport function useGeocode() {\n const [state, setState] = useState<GeocodeState>({\n status: 'idle',\n results: null,\n error: null,\n });\n\n const abortRef = useRef<AbortController | null>(null);\n\n useEffect(() => () => { abortRef.current?.abort(); }, []);\n\n const search = useCallback(async (query: GeocodeQuery) => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setState({ status: 'loading', results: null, error: null });\n\n try {\n const url = new URL(GEOCODE_URL);\n\n if (query.queryString) {\n url.searchParams.set('queryString', query.queryString);\n } else {\n if (query.num) url.searchParams.set('num', query.num);\n if (query.street) url.searchParams.set('street', query.street);\n if (query.zip) url.searchParams.set('zip', query.zip);\n if (query.locality) url.searchParams.set('locality', query.locality);\n }\n\n const res = await fetch(url.toString(), { signal: controller.signal });\n\n if (!res.ok) {\n throw new Error(`Geocode request failed: ${res.status} ${res.statusText}`);\n }\n\n const data = (await res.json()) as {\n results?: Array<{\n easting: number;\n northing: number;\n accuracy?: number;\n AddressDetails?: {\n street?: string;\n number?: string;\n zip?: string;\n locality?: string;\n };\n }>;\n };\n\n const results: GeocodeResultItem[] = (data.results ?? []).map((r) => ({\n latLon: lurefToLatLon(r.easting, r.northing),\n easting: r.easting,\n northing: r.northing,\n accuracy: r.accuracy ?? 0,\n street: r.AddressDetails?.street,\n num: r.AddressDetails?.number,\n zip: r.AddressDetails?.zip,\n locality: r.AddressDetails?.locality,\n }));\n\n setState({ status: 'success', results, error: null });\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n setState({\n status: 'error',\n results: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState({ status: 'idle', results: null, error: null });\n }, []);\n\n return { state, search, reset };\n}\n"],"names":["LUX_SCRIPT_URL","LUX_SCRIPT_ID","loadPromise","loadLuxApi","resolve","reject","pollForLux","script","attempts","check","useLuxApi","state","setState","useState","useEffect","cancelled","err","FE","FN","LON0","LAT0","SCALE","A","F","B","E2","DX","DY","DZ","degToRad","d","radToDeg","r","wgs84ToEd50","lat","lon","latR","lonR","sinLat","cosLat","sinLon","cosLon","aWgs","fWgs","bWgs","e2Wgs","da","df","N","dLat","dLon","ed50ToWgs84","M","tmForward","lon0R","lat0R","tanLat","T","C","dl","XX","meridianArc","M0","easting","northing","tmInverse","x","y","mu","e1","phi1","sinPhi1","cosPhi1","tanPhi1","N1","T1","C1","R1","D","e2","e4","e6","latLonToLuref","ed50","lurefToLatLon","LUXEMBOURG_CITY","DEFAULT_BG_LAYER","GeoportailMap","forwardRef","center","zoom","bgLayer","markerMode","markerPosition","onMarkerPlace","className","style","layers","ref","mapId","useId","luxApi","mapRef","useRef","markerLayerRef","clickListenerRef","onMarkerPlaceRef","lux","mapInstance","map","clearMarker","initialPos","placeMarker","handler","args","evt","e","n","latLon","_a","useImperativeHandle","coords","z","jsx","jsxs","position","layerRef","ol","overlay","el","PIN_SVG","_map","REVERSE_GEOCODE_URL","useReverseGeocode","abortRef","lookup","useCallback","controller","url","res","first","_b","reset","GEOCODE_URL","useGeocode","search","query","results","_c","_d"],"mappings":"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"}
1
+ {"version":3,"file":"react-geoportail.js","sources":["../src/utils/loader.ts","../src/hooks/useLuxApi.ts","../src/utils/coordinates.ts","../src/components/GeoportailMap.tsx","../src/hooks/useReverseGeocode.ts","../src/hooks/useGeocode.ts"],"sourcesContent":["/**\n * Dynamically injects the Geoportail v3 API script and resolves when\n * the global `lux` namespace is available.\n */\n\nconst LUX_SCRIPT_URL = 'https://apiv3.geoportail.lu/apiv3loader.js';\nconst LUX_SCRIPT_ID = 'geoportail-apiv3-loader';\n\nlet loadPromise: Promise<void> | null = null;\n\n/**\n * Loads the Geoportail apiv3loader.js script exactly once.\n * Safe to call multiple times — subsequent calls return the same promise.\n */\nexport function loadLuxApi(): Promise<void> {\n if (loadPromise) return loadPromise;\n\n loadPromise = new Promise<void>((resolve, reject) => {\n // Already loaded (e.g. added manually in HTML)\n if (typeof window !== 'undefined' && window.lux) {\n resolve();\n return;\n }\n\n if (typeof document === 'undefined') {\n reject(new Error('loadLuxApi must run in a browser environment'));\n return;\n }\n\n // Avoid duplicate script tags\n if (document.getElementById(LUX_SCRIPT_ID)) {\n pollForLux(resolve, reject);\n return;\n }\n\n const script = document.createElement('script');\n script.id = LUX_SCRIPT_ID;\n script.src = LUX_SCRIPT_URL;\n script.async = true;\n\n script.onload = () => pollForLux(resolve, reject);\n script.onerror = () => {\n loadPromise = null; // allow retry\n reject(new Error(`Failed to load Geoportail API from ${LUX_SCRIPT_URL}`));\n };\n\n document.head.appendChild(script);\n });\n\n return loadPromise;\n}\n\n/**\n * The lux API may initialise asynchronously after the script loads.\n * Poll until window.lux is defined (max ~5 s).\n */\nfunction pollForLux(resolve: () => void, reject: (err: Error) => void): void {\n const maxAttempts = 100;\n let attempts = 0;\n\n const check = () => {\n if (window.lux) {\n resolve();\n return;\n }\n if (attempts++ >= maxAttempts) {\n loadPromise = null;\n reject(new Error('Timed out waiting for window.lux to be defined'));\n return;\n }\n setTimeout(check, 50);\n };\n\n check();\n}\n","import { useState, useEffect } from 'react';\nimport { loadLuxApi } from '../utils/loader';\nimport type { LuxNamespace } from '../types/lux';\n\nexport type LuxApiState =\n | { status: 'loading'; lux: null; error: null }\n | { status: 'ready'; lux: LuxNamespace; error: null }\n | { status: 'error'; lux: null; error: Error };\n\n/**\n * Loads the Geoportail apiv3 script and returns the `lux` namespace once ready.\n * Deduplicates the script load — safe to call from multiple components.\n */\nexport function useLuxApi(): LuxApiState {\n const [state, setState] = useState<LuxApiState>({\n status: 'loading',\n lux: null,\n error: null,\n });\n\n useEffect(() => {\n let cancelled = false;\n\n loadLuxApi()\n .then(() => {\n if (!cancelled) {\n setState({ status: 'ready', lux: window.lux!, error: null });\n }\n })\n .catch((err: unknown) => {\n if (!cancelled) {\n setState({\n status: 'error',\n lux: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, []);\n\n return state;\n}\n","/**\n * Coordinate conversion between EPSG:2169 (Luxembourg TM) and EPSG:4326 (WGS84).\n *\n * The Geoportail API operates internally in EPSG:2169 but also accepts\n * position arrays in EPSG:4326 via `lux.Map` constructor options.\n *\n * For precise server-side conversion the REST API itself is authoritative.\n * The formulas below use a 6-parameter Helmert + transverse Mercator approach\n * sufficient for Luxembourg's territory (~50 km radius).\n *\n * Reference: IGN-L / ACT official parameters for LUREF (EPSG:2169).\n */\n\n// LUREF / Luxembourg TM projection constants (EPSG:2169)\nconst FE = 80000; // False Easting (m)\nconst FN = 100000; // False Northing (m)\nconst LON0 = 6.16666666666667; // Central meridian (6°10') in degrees\nconst LAT0 = 49.8333333333333; // Latitude of origin (49°50') in degrees\nconst SCALE = 1.0; // Scale factor at central meridian\nconst A = 6378388.0; // Semi-major axis (Hayford / International 1924)\nconst F = 1 / 297.0; // Flattening\nconst B = A * (1 - F); // Semi-minor axis\n\nconst E2 = 1 - (B * B) / (A * A); // First eccentricity squared\n\n// Helmert 7-parameter shift: ETRS89 -> ED50 (approximate for Luxembourg)\n// These values transform WGS84 (≈ETRS89) to ED50 used by LUREF\nconst DX = -87.0;\nconst DY = -98.0;\nconst DZ = -121.0;\n\nfunction degToRad(d: number): number {\n return (d * Math.PI) / 180;\n}\n\nfunction radToDeg(r: number): number {\n return (r * 180) / Math.PI;\n}\n\n/**\n * Molodensky transformation: WGS84 -> ED50 (approximate, <1 m accuracy).\n */\nfunction wgs84ToEd50(\n lat: number,\n lon: number\n): { lat: number; lon: number } {\n const latR = degToRad(lat);\n const lonR = degToRad(lon);\n\n const sinLat = Math.sin(latR);\n const cosLat = Math.cos(latR);\n const sinLon = Math.sin(lonR);\n const cosLon = Math.cos(lonR);\n\n const aWgs = 6378137.0;\n const fWgs = 1 / 298.257223563;\n const bWgs = aWgs * (1 - fWgs);\n const e2Wgs = 1 - (bWgs * bWgs) / (aWgs * aWgs);\n\n const da = A - aWgs;\n const df = F - fWgs;\n\n const N = aWgs / Math.sqrt(1 - e2Wgs * sinLat * sinLat);\n const M =\n (aWgs * (1 - e2Wgs)) /\n Math.pow(1 - e2Wgs * sinLat * sinLat, 1.5);\n\n const dLat =\n (1 / M) *\n (N * (e2Wgs / (1 - e2Wgs)) * sinLat * cosLat * (da / aWgs) +\n (N / aWgs + 1) * sinLat * cosLat * df * (aWgs / bWgs) -\n (DX * sinLat * cosLon + DY * sinLat * sinLon - DZ * cosLat));\n\n const dLon =\n (1 / (N * cosLat)) * (-DX * sinLon + DY * cosLon);\n\n return {\n lat: lat + radToDeg(dLat),\n lon: lon + radToDeg(dLon),\n };\n}\n\n/**\n * ED50 -> WGS84 (inverse Molodensky, approximate).\n */\nfunction ed50ToWgs84(\n lat: number,\n lon: number\n): { lat: number; lon: number } {\n const latR = degToRad(lat);\n const lonR = degToRad(lon);\n\n const sinLat = Math.sin(latR);\n const cosLat = Math.cos(latR);\n const sinLon = Math.sin(lonR);\n const cosLon = Math.cos(lonR);\n\n const N = A / Math.sqrt(1 - E2 * sinLat * sinLat);\n const M = (A * (1 - E2)) / Math.pow(1 - E2 * sinLat * sinLat, 1.5);\n\n const da = 6378137.0 - A;\n const fWgs = 1 / 298.257223563;\n const df = fWgs - F;\n\n const dLat =\n (1 / M) *\n (N * (E2 / (1 - E2)) * sinLat * cosLat * (da / A) +\n (N / A + 1) * sinLat * cosLat * df * (A / B) +\n (DX * sinLat * cosLon + DY * sinLat * sinLon - DZ * cosLat));\n\n const dLon =\n (1 / (N * cosLat)) * (DX * sinLon - DY * cosLon);\n\n return {\n lat: lat + radToDeg(dLat),\n lon: lon + radToDeg(dLon),\n };\n}\n\n/**\n * Transverse Mercator forward projection (ED50 lat/lon -> LUREF easting/northing).\n */\nfunction tmForward(lat: number, lon: number): { easting: number; northing: number } {\n const latR = degToRad(lat);\n const lonR = degToRad(lon);\n const lon0R = degToRad(LON0);\n const lat0R = degToRad(LAT0);\n\n const sinLat = Math.sin(latR);\n const cosLat = Math.cos(latR);\n const tanLat = Math.tan(latR);\n\n const N = A / Math.sqrt(1 - E2 * sinLat * sinLat);\n const T = tanLat * tanLat;\n const C = (E2 / (1 - E2)) * cosLat * cosLat;\n const dl = lonR - lon0R;\n const XX = cosLat * dl;\n\n // Meridian arc from equator to lat\n const M = meridianArc(latR);\n const M0 = meridianArc(lat0R);\n\n const easting =\n FE +\n SCALE *\n N *\n (XX +\n (XX * XX * XX * (1 - T + C)) / 6 +\n (XX * XX * XX * XX * XX * (5 - 18 * T + T * T + 72 * C)) / 120);\n\n const northing =\n FN +\n SCALE *\n (M -\n M0 +\n N *\n tanLat *\n (XX * XX / 2 +\n (XX * XX * XX * XX * (5 - T + 9 * C + 4 * C * C)) / 24 +\n (XX * XX * XX * XX * XX * XX *\n (61 - 58 * T + T * T + 600 * C)) /\n 720));\n\n return { easting, northing };\n}\n\n/**\n * Transverse Mercator inverse projection (LUREF easting/northing -> ED50 lat/lon).\n */\nfunction tmInverse(easting: number, northing: number): { lat: number; lon: number } {\n const x = easting - FE;\n const y = northing - FN;\n const lat0R = degToRad(LAT0);\n const lon0R = degToRad(LON0);\n const M0 = meridianArc(lat0R);\n\n // Footpoint latitude\n const M1 = M0 + y / SCALE;\n const mu = M1 / (A * (1 - E2 / 4 - (3 * E2 * E2) / 64));\n\n const e1 = (1 - Math.sqrt(1 - E2)) / (1 + Math.sqrt(1 - E2));\n const phi1 =\n mu +\n ((3 * e1) / 2 - (27 * e1 * e1 * e1) / 32) * Math.sin(2 * mu) +\n ((21 * e1 * e1) / 16 - (55 * e1 * e1 * e1 * e1) / 32) *\n Math.sin(4 * mu) +\n ((151 * e1 * e1 * e1) / 96) * Math.sin(6 * mu);\n\n const sinPhi1 = Math.sin(phi1);\n const cosPhi1 = Math.cos(phi1);\n const tanPhi1 = Math.tan(phi1);\n\n const N1 = A / Math.sqrt(1 - E2 * sinPhi1 * sinPhi1);\n const T1 = tanPhi1 * tanPhi1;\n const C1 = (E2 / (1 - E2)) * cosPhi1 * cosPhi1;\n const R1 =\n (A * (1 - E2)) / Math.pow(1 - E2 * sinPhi1 * sinPhi1, 1.5);\n const D = x / (N1 * SCALE);\n\n const lat =\n phi1 -\n ((N1 * tanPhi1) / R1) *\n (D * D / 2 -\n (D * D * D * D * (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1)) / 24 +\n (D * D * D * D * D * D *\n (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1)) /\n 720);\n\n const lon =\n lon0R +\n (D -\n (D * D * D * (1 + 2 * T1 + C1)) / 6 +\n (D * D * D * D * D * (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1)) / 120) /\n cosPhi1;\n\n return { lat: radToDeg(lat), lon: radToDeg(lon) };\n}\n\nfunction meridianArc(latR: number): number {\n const e2 = E2;\n const e4 = e2 * e2;\n const e6 = e4 * e2;\n return (\n A *\n ((1 - e2 / 4 - (3 * e4) / 64 - (5 * e6) / 256) * latR -\n ((3 * e2) / 8 + (3 * e4) / 32 + (45 * e6) / 1024) *\n Math.sin(2 * latR) +\n ((15 * e4) / 256 + (45 * e6) / 1024) * Math.sin(4 * latR) -\n ((35 * e6) / 3072) * Math.sin(6 * latR))\n );\n}\n\n/**\n * Convert WGS84 (lat/lon) to EPSG:2169 (easting/northing).\n */\nexport function latLonToLuref(\n lat: number,\n lon: number\n): { easting: number; northing: number } {\n const ed50 = wgs84ToEd50(lat, lon);\n return tmForward(ed50.lat, ed50.lon);\n}\n\n/**\n * Convert EPSG:2169 (easting/northing) to WGS84 (lat/lon).\n */\nexport function lurefToLatLon(\n easting: number,\n northing: number\n): { lat: number; lon: number } {\n const ed50 = tmInverse(easting, northing);\n return ed50ToWgs84(ed50.lat, ed50.lon);\n}\n","import React, {\n useEffect,\n useRef,\n forwardRef,\n useImperativeHandle,\n} from 'react';\nimport { useLuxApi } from '../hooks/useLuxApi';\nimport { lurefToLatLon, latLonToLuref } from '../utils/coordinates';\nimport type { LatLon, MapClickHandler, MarkerMode } from '../types';\nimport type { LuxMapInstance } from '../types/lux';\n\nexport interface GeoportailMapProps {\n /**\n * Initial center of the map.\n * Defaults to Luxembourg City (lat: 49.6116, lon: 6.1319).\n */\n center?: LatLon;\n\n /** Initial zoom level (1–20). Default: 12 */\n zoom?: number;\n\n /**\n * Background layer identifier.\n * Default: 'basemap_2015_global'\n */\n bgLayer?: string;\n\n /**\n * Controls pin/marker behaviour:\n * - 'none' — no marker\n * - 'fixed' — show a marker at `markerPosition` (does not move on click)\n * - 'click' — user clicks map to place/move the pin; fires `onMarkerPlace`\n *\n * Default: 'none'\n */\n markerMode?: MarkerMode;\n\n /**\n * Position of the marker when `markerMode` is 'fixed' or to pre-set an\n * initial pin when `markerMode` is 'click'.\n */\n markerPosition?: LatLon;\n\n /**\n * Called whenever the user places a pin (markerMode === 'click').\n * Receives the WGS84 lat/lon of the clicked point.\n */\n onMarkerPlace?: MapClickHandler;\n\n /** CSS class applied to the map container div */\n className?: string;\n\n /** Inline styles for the map container div */\n style?: React.CSSProperties;\n\n /** Additional numeric layer IDs to add on top of the background */\n layers?: number[];\n}\n\nexport interface GeoportailMapHandle {\n /** Returns the underlying lux.Map instance (or null before ready) */\n getLuxMap(): LuxMapInstance | null;\n /** Programmatically move the map center */\n setCenter(coords: LatLon): void;\n /** Programmatically set zoom */\n setZoom(zoom: number): void;\n}\n\nconst LUXEMBOURG_CITY: LatLon = { lat: 49.6116, lon: 6.1319 };\nconst DEFAULT_BG_LAYER = 'basemap_2015_global';\n\n/**\n * Renders a Geoportail Luxembourg map inside a React component.\n *\n * Expose a ref (`GeoportailMapHandle`) to access the underlying lux.Map instance\n * or imperatively control the view.\n */\nexport const GeoportailMap = forwardRef<GeoportailMapHandle, GeoportailMapProps>(\n function GeoportailMap(\n {\n center = LUXEMBOURG_CITY,\n zoom = 12,\n bgLayer = DEFAULT_BG_LAYER,\n markerMode = 'none',\n markerPosition,\n onMarkerPlace,\n className,\n style,\n layers,\n },\n ref\n ) {\n const luxApi = useLuxApi();\n const mapContainerRef = useRef<HTMLDivElement | null>(null);\n const mapRef = useRef<LuxMapInstance | null>(null);\n const markerLayerRef = useRef<unknown>(null);\n const clickListenerRef = useRef<((...args: unknown[]) => void) | null>(null);\n\n // Keep stable refs to callbacks so effects don't re-run on every render\n const onMarkerPlaceRef = useRef(onMarkerPlace);\n onMarkerPlaceRef.current = onMarkerPlace;\n\n // ------------------------------------------------------------------ map init\n useEffect(() => {\n if (luxApi.status !== 'ready') return;\n if (!mapContainerRef.current) return;\n\n const lux = luxApi.lux;\n const { easting, northing } = latLonToLuref(center.lat, center.lon);\n\n const mapInstance = new lux.Map({\n target: mapContainerRef.current,\n bgLayer,\n zoom,\n position: [easting, northing],\n ...(layers && layers.length > 0\n ? { layers, layerOpacities: layers.map(() => 1) }\n : {}),\n });\n\n mapRef.current = mapInstance;\n\n return () => {\n // lux.Map does not expose a destroy(); we clear our references\n mapRef.current = null;\n markerLayerRef.current = null;\n clickListenerRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [luxApi.status]);\n\n // ------------------------------------------------------------------ marker\n useEffect(() => {\n const map = mapRef.current;\n if (!map || luxApi.status !== 'ready') return;\n\n // Remove previous click listener\n if (clickListenerRef.current) {\n map.un('singleclick', clickListenerRef.current);\n clickListenerRef.current = null;\n }\n\n if (markerMode === 'none') {\n clearMarker(map, markerLayerRef);\n return;\n }\n\n const initialPos = markerPosition ?? (markerMode === 'fixed' ? center : undefined);\n if (initialPos) {\n placeMarker(map, initialPos, markerLayerRef);\n }\n\n if (markerMode === 'click') {\n const handler = (...args: unknown[]) => {\n // OpenLayers MapBrowserEvent — coordinate is in map projection (EPSG:2169)\n const evt = args[0] as { coordinate?: [number, number] };\n if (!evt.coordinate) return;\n\n const [e, n] = evt.coordinate;\n const latLon = lurefToLatLon(e, n);\n placeMarker(map, latLon, markerLayerRef);\n onMarkerPlaceRef.current?.(latLon);\n };\n\n clickListenerRef.current = handler;\n map.on('singleclick', handler);\n }\n }, [\n luxApi.status,\n markerMode,\n markerPosition?.lat,\n markerPosition?.lon,\n center.lat,\n center.lon,\n ]);\n\n // ------------------------------------------------------------------ imperative handle\n useImperativeHandle(ref, () => ({\n getLuxMap: () => mapRef.current,\n setCenter(coords: LatLon) {\n const map = mapRef.current;\n if (!map) return;\n const { easting, northing } = latLonToLuref(coords.lat, coords.lon);\n map.getView().setCenter([easting, northing]);\n },\n setZoom(z: number) {\n mapRef.current?.getView().setZoom(z);\n },\n }));\n\n // ------------------------------------------------------------------ render\n if (luxApi.status === 'error') {\n return (\n <div\n className={className}\n style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#f5f5f5', ...style }}\n >\n <span style={{ color: '#c00' }}>\n Failed to load Geoportail API: {luxApi.error.message}\n </span>\n </div>\n );\n }\n\n return (\n <div style={{ position: 'relative', ...style }} className={className}>\n {luxApi.status === 'loading' && (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'rgba(255,255,255,0.7)',\n zIndex: 10,\n }}\n >\n <span>Loading map…</span>\n </div>\n )}\n <div\n ref={mapContainerRef}\n style={{ width: '100%', height: '100%' }}\n />\n </div>\n );\n }\n);\n\n// ------------------------------------------------------------------ helpers\n\n/**\n * Place/move an SVG pin marker on the map.\n * lux.Map is built on OpenLayers 3 — we use ol.Overlay for the marker.\n */\nfunction placeMarker(\n map: LuxMapInstance,\n position: LatLon,\n layerRef: React.MutableRefObject<unknown>\n): void {\n // Prefer ol (OpenLayers global) if available\n const ol = (window as unknown as { ol?: OlLike }).ol;\n if (!ol) return;\n\n const { easting, northing } = latLonToLuref(position.lat, position.lon);\n\n // Reuse existing overlay or create a new one\n if (layerRef.current) {\n const overlay = layerRef.current as OlOverlay;\n overlay.setPosition([easting, northing]);\n return;\n }\n\n const el = document.createElement('div');\n el.innerHTML = PIN_SVG;\n el.style.cssText =\n 'cursor:pointer;transform:translate(-50%,-100%);line-height:0;';\n\n const overlay = new ol.Overlay({\n element: el,\n positioning: 'bottom-center',\n stopEvent: false,\n });\n\n overlay.setPosition([easting, northing]);\n (map as unknown as { addOverlay(o: OlOverlay): void }).addOverlay(overlay);\n layerRef.current = overlay;\n}\n\nfunction clearMarker(\n _map: LuxMapInstance,\n layerRef: React.MutableRefObject<unknown>\n): void {\n if (!layerRef.current) return;\n const ol = (window as unknown as { ol?: OlLike }).ol;\n if (!ol) return;\n // Overlay doesn't have a built-in remove on the overlay itself;\n // set position to undefined to hide it\n (layerRef.current as OlOverlay).setPosition(undefined);\n layerRef.current = null;\n}\n\n// Minimal pin SVG (red teardrop, 30×40)\nconst PIN_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"30\" height=\"40\" viewBox=\"0 0 30 40\">\n <path d=\"M15 0C7.268 0 1 6.268 1 14c0 10.5 14 26 14 26S29 24.5 29 14C29 6.268 22.732 0 15 0z\"\n fill=\"#e53935\" stroke=\"#b71c1c\" stroke-width=\"1.5\"/>\n <circle cx=\"15\" cy=\"14\" r=\"5\" fill=\"white\"/>\n</svg>`;\n\n// Minimal OL type stubs used only within this file\ninterface OlOverlay {\n setPosition(pos: [number, number] | undefined): void;\n}\n\ninterface OlLike {\n Overlay: new (opts: { element: HTMLElement; positioning: string; stopEvent: boolean }) => OlOverlay;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { latLonToLuref } from '../utils/coordinates';\nimport type { LatLon, Address } from '../types';\n\nconst REVERSE_GEOCODE_URL = 'https://api.geoportail.lu/geocoder/reverseGeocode';\n\nexport type ReverseGeocodeState =\n | { status: 'idle'; address: null; error: null }\n | { status: 'loading'; address: null; error: null }\n | { status: 'success'; address: Address; error: null }\n | { status: 'error'; address: null; error: Error };\n\n/**\n * Hook for reverse geocoding: convert a WGS84 lat/lon to a Luxembourg address.\n *\n * Uses the Geoportail REST reverse geocode endpoint — no API key required.\n *\n * @example\n * const { state, lookup } = useReverseGeocode();\n * // ...\n * lookup({ lat: 49.6116, lon: 6.1319 });\n * if (state.status === 'success') console.log(state.address.label);\n */\nexport function useReverseGeocode() {\n const [state, setState] = useState<ReverseGeocodeState>({\n status: 'idle',\n address: null,\n error: null,\n });\n\n const abortRef = useRef<AbortController | null>(null);\n\n useEffect(() => () => { abortRef.current?.abort(); }, []);\n\n const lookup = useCallback(async (position: LatLon) => {\n // Cancel any in-flight request\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setState({ status: 'loading', address: null, error: null });\n\n try {\n const { easting, northing } = latLonToLuref(position.lat, position.lon);\n\n const url = new URL(REVERSE_GEOCODE_URL);\n url.searchParams.set('easting', String(easting));\n url.searchParams.set('northing', String(northing));\n\n const res = await fetch(url.toString(), { signal: controller.signal });\n\n if (!res.ok) {\n throw new Error(`Reverse geocode request failed: ${res.status} ${res.statusText}`);\n }\n\n const data = (await res.json()) as {\n results?: Array<{\n easting: number;\n northing: number;\n name?: string;\n distance?: number;\n }>;\n };\n\n const first = data.results?.[0];\n if (!first) {\n throw new Error('No results returned for this position');\n }\n\n setState({\n status: 'success',\n address: {\n label: first.name ?? '',\n distance: first.distance ?? 0,\n easting: first.easting,\n northing: first.northing,\n },\n error: null,\n });\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n setState({\n status: 'error',\n address: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState({ status: 'idle', address: null, error: null });\n }, []);\n\n return { state, lookup, reset };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { lurefToLatLon } from '../utils/coordinates';\nimport type { GeocodeQuery, LatLon } from '../types';\n\nconst GEOCODE_URL = 'https://apiv3.geoportail.lu/geocode/search';\n\nexport interface GeocodeResultItem {\n latLon: LatLon;\n easting: number;\n northing: number;\n accuracy: number;\n street?: string;\n num?: string;\n zip?: string;\n locality?: string;\n}\n\nexport type GeocodeState =\n | { status: 'idle'; results: null; error: null }\n | { status: 'loading'; results: null; error: null }\n | { status: 'success'; results: GeocodeResultItem[]; error: null }\n | { status: 'error'; results: null; error: Error };\n\n/**\n * Hook for forward geocoding: search for a Luxembourg address and get coordinates.\n *\n * @example\n * const { state, search } = useGeocode();\n * search({ queryString: '1 rue du Fort Thüngen, Luxembourg' });\n * if (state.status === 'success') {\n * const { lat, lon } = state.results[0].latLon;\n * }\n */\nexport function useGeocode() {\n const [state, setState] = useState<GeocodeState>({\n status: 'idle',\n results: null,\n error: null,\n });\n\n const abortRef = useRef<AbortController | null>(null);\n\n useEffect(() => () => { abortRef.current?.abort(); }, []);\n\n const search = useCallback(async (query: GeocodeQuery) => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setState({ status: 'loading', results: null, error: null });\n\n try {\n const url = new URL(GEOCODE_URL);\n\n if (query.queryString) {\n url.searchParams.set('queryString', query.queryString);\n } else {\n if (query.num) url.searchParams.set('num', query.num);\n if (query.street) url.searchParams.set('street', query.street);\n if (query.zip) url.searchParams.set('zip', query.zip);\n if (query.locality) url.searchParams.set('locality', query.locality);\n }\n\n const res = await fetch(url.toString(), { signal: controller.signal });\n\n if (!res.ok) {\n throw new Error(`Geocode request failed: ${res.status} ${res.statusText}`);\n }\n\n const data = (await res.json()) as {\n results?: Array<{\n easting: number;\n northing: number;\n accuracy?: number;\n AddressDetails?: {\n street?: string;\n number?: string;\n zip?: string;\n locality?: string;\n };\n }>;\n };\n\n const results: GeocodeResultItem[] = (data.results ?? []).map((r) => ({\n latLon: lurefToLatLon(r.easting, r.northing),\n easting: r.easting,\n northing: r.northing,\n accuracy: r.accuracy ?? 0,\n street: r.AddressDetails?.street,\n num: r.AddressDetails?.number,\n zip: r.AddressDetails?.zip,\n locality: r.AddressDetails?.locality,\n }));\n\n setState({ status: 'success', results, error: null });\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n setState({\n status: 'error',\n results: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState({ status: 'idle', results: null, error: null });\n }, []);\n\n return { state, search, reset };\n}\n"],"names":["LUX_SCRIPT_URL","LUX_SCRIPT_ID","loadPromise","loadLuxApi","resolve","reject","pollForLux","script","attempts","check","useLuxApi","state","setState","useState","useEffect","cancelled","err","FE","FN","LON0","LAT0","SCALE","A","B","E2","DX","DY","DZ","degToRad","d","radToDeg","r","wgs84ToEd50","lat","lon","latR","lonR","sinLat","cosLat","sinLon","cosLon","aWgs","fWgs","bWgs","e2Wgs","da","df","N","dLat","dLon","ed50ToWgs84","M","tmForward","lon0R","lat0R","tanLat","T","C","dl","XX","meridianArc","M0","easting","northing","tmInverse","x","y","mu","e1","phi1","sinPhi1","cosPhi1","tanPhi1","N1","T1","C1","R1","D","e2","e4","e6","latLonToLuref","ed50","lurefToLatLon","LUXEMBOURG_CITY","DEFAULT_BG_LAYER","GeoportailMap","forwardRef","center","zoom","bgLayer","markerMode","markerPosition","onMarkerPlace","className","style","layers","ref","luxApi","mapContainerRef","useRef","mapRef","markerLayerRef","clickListenerRef","onMarkerPlaceRef","lux","mapInstance","map","clearMarker","initialPos","placeMarker","handler","args","evt","e","n","latLon","_a","useImperativeHandle","coords","z","jsx","jsxs","position","layerRef","ol","overlay","el","PIN_SVG","_map","REVERSE_GEOCODE_URL","useReverseGeocode","abortRef","lookup","useCallback","controller","url","res","first","_b","reset","GEOCODE_URL","useGeocode","search","query","results","_c","_d"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- (function(x,v){typeof exports=="object"&&typeof module<"u"?v(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],v):(x=typeof globalThis<"u"?globalThis:x||self,v(x.ReactGeoportail={},x.jsxRuntime,x.React))})(this,function(x,v,w){"use strict";const F="https://apiv3.geoportail.lu/apiv3loader.js",z="geoportail-apiv3-loader";let S=null;function N(){return S||(S=new Promise((c,t)=>{if(typeof window<"u"&&window.lux){c();return}if(typeof document>"u"){t(new Error("loadLuxApi must run in a browser environment"));return}if(document.getElementById(z)){W(c,t);return}const n=document.createElement("script");n.id=z,n.src=F,n.async=!0,n.onload=()=>W(c,t),n.onerror=()=>{S=null,t(new Error(`Failed to load Geoportail API from ${F}`))},document.head.appendChild(n)}),S)}function W(c,t){let l=0;const a=()=>{if(window.lux){c();return}if(l++>=100){S=null,t(new Error("Timed out waiting for window.lux to be defined"));return}setTimeout(a,50)};a()}function X(){const[c,t]=w.useState({status:"loading",lux:null,error:null});return w.useEffect(()=>{let n=!1;return N().then(()=>{n||t({status:"ready",lux:window.lux,error:null})}).catch(l=>{n||t({status:"error",lux:null,error:l instanceof Error?l:new Error(String(l))})}),()=>{n=!0}},[]),c}const $=8e4,B=1e5,k=6.16666666666667,q=49.8333333333333,G=1,E=6378388,O=1/297,P=E*(1-O),m=1-P*P/(E*E),D=-87,_=-98,V=-121;function R(c){return c*Math.PI/180}function T(c){return c*180/Math.PI}function K(c,t){const n=R(c),l=R(t),a=Math.sin(n),e=Math.cos(n),p=Math.sin(l),g=Math.cos(l),s=6378137,r=1/298.257223563,h=s*(1-r),f=1-h*h/(s*s),u=E-s,o=O-r,d=s/Math.sqrt(1-f*a*a),L=1/(s*(1-f)/Math.pow(1-f*a*a,1.5))*(d*(f/(1-f))*a*e*(u/s)+(d/s+1)*a*e*o*(s/h)-(D*a*g+_*a*p-V*e)),y=1/(d*e)*(-D*p+_*g);return{lat:c+T(L),lon:t+T(y)}}function Q(c,t){const n=R(c),l=R(t),a=Math.sin(n),e=Math.cos(n),p=Math.sin(l),g=Math.cos(l),s=E/Math.sqrt(1-m*a*a),r=E*(1-m)/Math.pow(1-m*a*a,1.5),h=6378137-E,u=1/298.257223563-O,o=1/r*(s*(m/(1-m))*a*e*(h/E)+(s/E+1)*a*e*u*(E/P)+(D*a*g+_*a*p-V*e)),d=1/(s*e)*(D*p-_*g);return{lat:c+T(o),lon:t+T(d)}}function tt(c,t){const n=R(c),l=R(t),a=R(k),e=R(q),p=Math.sin(n),g=Math.cos(n),s=Math.tan(n),r=E/Math.sqrt(1-m*p*p),h=s*s,f=m/(1-m)*g*g,u=l-a,o=g*u,d=U(n),M=U(e),L=$+G*r*(o+o*o*o*(1-h+f)/6+o*o*o*o*o*(5-18*h+h*h+72*f)/120),y=B+G*(d-M+r*s*(o*o/2+o*o*o*o*(5-h+9*f+4*f*f)/24+o*o*o*o*o*o*(61-58*h+h*h+600*f)/720));return{easting:L,northing:y}}function nt(c,t){const n=c-$,l=t-B,a=R(q),e=R(k),s=(U(a)+l/G)/(E*(1-m/4-3*m*m/64)),r=(1-Math.sqrt(1-m))/(1+Math.sqrt(1-m)),h=s+(3*r/2-27*r*r*r/32)*Math.sin(2*s)+(21*r*r/16-55*r*r*r*r/32)*Math.sin(4*s)+151*r*r*r/96*Math.sin(6*s),f=Math.sin(h),u=Math.cos(h),o=Math.tan(h),d=E/Math.sqrt(1-m*f*f),M=o*o,L=m/(1-m)*u*u,y=E*(1-m)/Math.pow(1-m*f*f,1.5),i=n/(d*G),b=h-d*o/y*(i*i/2-i*i*i*i*(5+3*M+10*L-4*L*L)/24+i*i*i*i*i*i*(61+90*M+298*L+45*M*M)/720),A=e+(i-i*i*i*(1+2*M+L)/6+i*i*i*i*i*(5-2*L+28*M-3*L*L)/120)/u;return{lat:T(b),lon:T(A)}}function U(c){const t=m,n=t*t,l=n*t;return E*((1-t/4-3*n/64-5*l/256)*c-(3*t/8+3*n/32+45*l/1024)*Math.sin(2*c)+(15*n/256+45*l/1024)*Math.sin(4*c)-35*l/3072*Math.sin(6*c))}function C(c,t){const n=K(c,t);return tt(n.lat,n.lon)}function j(c,t){const n=nt(c,t);return Q(n.lat,n.lon)}const et={lat:49.6116,lon:6.1319},st="basemap_2015_global",ot=w.forwardRef(function({center:t=et,zoom:n=12,bgLayer:l=st,markerMode:a="none",markerPosition:e,onMarkerPlace:p,className:g,style:s,layers:r},h){const u=`gp-map-${w.useId().replace(/:/g,"")}`,o=X(),d=w.useRef(null),M=w.useRef(null),L=w.useRef(null),y=w.useRef(p);return y.current=p,w.useEffect(()=>{if(o.status!=="ready")return;const i=o.lux,{easting:b,northing:A}=C(t.lat,t.lon),I=new i.Map({target:u,bgLayer:l,zoom:n,position:[b,A],...r&&r.length>0?{layers:r,layerOpacities:r.map(()=>1)}:{}});return d.current=I,()=>{d.current=null,M.current=null,L.current=null}},[o.status,u]),w.useEffect(()=>{const i=d.current;if(!i||o.status!=="ready")return;if(L.current&&(i.un("singleclick",L.current),L.current=null),a==="none"){rt(i,M);return}const b=e??(a==="fixed"?t:void 0);if(b&&Y(i,b,M),a==="click"){const A=(...I)=>{var J;const Z=I[0];if(!Z.coordinate)return;const[dt,ft]=Z.coordinate,H=j(dt,ft);Y(i,H,M),(J=y.current)==null||J.call(y,H)};L.current=A,i.on("singleclick",A)}},[o.status,a,e==null?void 0:e.lat,e==null?void 0:e.lon,t.lat,t.lon]),w.useImperativeHandle(h,()=>({getLuxMap:()=>d.current,setCenter(i){const b=d.current;if(!b)return;const{easting:A,northing:I}=C(i.lat,i.lon);b.getView().setCenter([A,I])},setZoom(i){var b;(b=d.current)==null||b.getView().setZoom(i)}})),o.status==="error"?v.jsx("div",{className:g,style:{display:"flex",alignItems:"center",justifyContent:"center",background:"#f5f5f5",...s},children:v.jsxs("span",{style:{color:"#c00"},children:["Failed to load Geoportail API: ",o.error.message]})}):v.jsxs("div",{style:{position:"relative",...s},className:g,children:[o.status==="loading"&&v.jsx("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(255,255,255,0.7)",zIndex:10},children:v.jsx("span",{children:"Loading map…"})}),v.jsx("div",{id:u,style:{width:"100%",height:"100%"}})]})});function Y(c,t,n){const l=window.ol;if(!l)return;const{easting:a,northing:e}=C(t.lat,t.lon);if(n.current){n.current.setPosition([a,e]);return}const p=document.createElement("div");p.innerHTML=ct,p.style.cssText="cursor:pointer;transform:translate(-50%,-100%);line-height:0;";const g=new l.Overlay({element:p,positioning:"bottom-center",stopEvent:!1});g.setPosition([a,e]),c.addOverlay(g),n.current=g}function rt(c,t){!t.current||!window.ol||(t.current.setPosition(void 0),t.current=null)}const ct=`<svg xmlns="http://www.w3.org/2000/svg" width="30" height="40" viewBox="0 0 30 40">
1
+ (function(b,R){typeof exports=="object"&&typeof module<"u"?R(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],R):(b=typeof globalThis<"u"?globalThis:b||self,R(b.ReactGeoportail={},b.jsxRuntime,b.React))})(this,function(b,R,w){"use strict";const j="https://apiv3.geoportail.lu/apiv3loader.js",F="geoportail-apiv3-loader";let S=null;function z(){return S||(S=new Promise((c,t)=>{if(typeof window<"u"&&window.lux){c();return}if(typeof document>"u"){t(new Error("loadLuxApi must run in a browser environment"));return}if(document.getElementById(F)){N(c,t);return}const n=document.createElement("script");n.id=F,n.src=j,n.async=!0,n.onload=()=>N(c,t),n.onerror=()=>{S=null,t(new Error(`Failed to load Geoportail API from ${j}`))},document.head.appendChild(n)}),S)}function N(c,t){let u=0;const a=()=>{if(window.lux){c();return}if(u++>=100){S=null,t(new Error("Timed out waiting for window.lux to be defined"));return}setTimeout(a,50)};a()}function W(){const[c,t]=w.useState({status:"loading",lux:null,error:null});return w.useEffect(()=>{let n=!1;return z().then(()=>{n||t({status:"ready",lux:window.lux,error:null})}).catch(u=>{n||t({status:"error",lux:null,error:u instanceof Error?u:new Error(String(u))})}),()=>{n=!0}},[]),c}const X=8e4,B=1e5,$=6.16666666666667,k=49.8333333333333,G=1,x=6378388,_=1/297,O=x*(1-_),M=1-O*O/(x*x),I=-87,D=-98,q=-121;function v(c){return c*Math.PI/180}function T(c){return c*180/Math.PI}function J(c,t){const n=v(c),u=v(t),a=Math.sin(n),e=Math.cos(n),p=Math.sin(u),g=Math.cos(u),s=6378137,r=1/298.257223563,f=s*(1-r),i=1-f*f/(s*s),d=x-s,o=_-r,h=s/Math.sqrt(1-i*a*a),L=1/(s*(1-i)/Math.pow(1-i*a*a,1.5))*(h*(i/(1-i))*a*e*(d/s)+(h/s+1)*a*e*o*(s/f)-(I*a*g+D*a*p-q*e)),m=1/(h*e)*(-I*p+D*g);return{lat:c+T(L),lon:t+T(m)}}function K(c,t){const n=v(c),u=v(t),a=Math.sin(n),e=Math.cos(n),p=Math.sin(u),g=Math.cos(u),s=x/Math.sqrt(1-M*a*a),r=x*(1-M)/Math.pow(1-M*a*a,1.5),f=6378137-x,d=1/298.257223563-_,o=1/r*(s*(M/(1-M))*a*e*(f/x)+(s/x+1)*a*e*d*(x/O)+(I*a*g+D*a*p-q*e)),h=1/(s*e)*(I*p-D*g);return{lat:c+T(o),lon:t+T(h)}}function Q(c,t){const n=v(c),u=v(t),a=v($),e=v(k),p=Math.sin(n),g=Math.cos(n),s=Math.tan(n),r=x/Math.sqrt(1-M*p*p),f=s*s,i=M/(1-M)*g*g,d=u-a,o=g*d,h=P(n),E=P(e),L=X+G*r*(o+o*o*o*(1-f+i)/6+o*o*o*o*o*(5-18*f+f*f+72*i)/120),m=B+G*(h-E+r*s*(o*o/2+o*o*o*o*(5-f+9*i+4*i*i)/24+o*o*o*o*o*o*(61-58*f+f*f+600*i)/720));return{easting:L,northing:m}}function tt(c,t){const n=c-X,u=t-B,a=v(k),e=v($),s=(P(a)+u/G)/(x*(1-M/4-3*M*M/64)),r=(1-Math.sqrt(1-M))/(1+Math.sqrt(1-M)),f=s+(3*r/2-27*r*r*r/32)*Math.sin(2*s)+(21*r*r/16-55*r*r*r*r/32)*Math.sin(4*s)+151*r*r*r/96*Math.sin(6*s),i=Math.sin(f),d=Math.cos(f),o=Math.tan(f),h=x/Math.sqrt(1-M*i*i),E=o*o,L=M/(1-M)*d*d,m=x*(1-M)/Math.pow(1-M*i*i,1.5),l=n/(h*G),y=f-h*o/m*(l*l/2-l*l*l*l*(5+3*E+10*L-4*L*L)/24+l*l*l*l*l*l*(61+90*E+298*L+45*E*E)/720),A=e+(l-l*l*l*(1+2*E+L)/6+l*l*l*l*l*(5-2*L+28*E-3*L*L)/120)/d;return{lat:T(y),lon:T(A)}}function P(c){const t=M,n=t*t,u=n*t;return x*((1-t/4-3*n/64-5*u/256)*c-(3*t/8+3*n/32+45*u/1024)*Math.sin(2*c)+(15*n/256+45*u/1024)*Math.sin(4*c)-35*u/3072*Math.sin(6*c))}function C(c,t){const n=J(c,t);return Q(n.lat,n.lon)}function U(c,t){const n=tt(c,t);return K(n.lat,n.lon)}const nt={lat:49.6116,lon:6.1319},et="basemap_2015_global",st=w.forwardRef(function({center:t=nt,zoom:n=12,bgLayer:u=et,markerMode:a="none",markerPosition:e,onMarkerPlace:p,className:g,style:s,layers:r},f){const i=W(),d=w.useRef(null),o=w.useRef(null),h=w.useRef(null),E=w.useRef(null),L=w.useRef(p);return L.current=p,w.useEffect(()=>{if(i.status!=="ready"||!d.current)return;const m=i.lux,{easting:l,northing:y}=C(t.lat,t.lon),A=new m.Map({target:d.current,bgLayer:u,zoom:n,position:[l,y],...r&&r.length>0?{layers:r,layerOpacities:r.map(()=>1)}:{}});return o.current=A,()=>{o.current=null,h.current=null,E.current=null}},[i.status]),w.useEffect(()=>{const m=o.current;if(!m||i.status!=="ready")return;if(E.current&&(m.un("singleclick",E.current),E.current=null),a==="none"){ot(m,h);return}const l=e??(a==="fixed"?t:void 0);if(l&&V(m,l,h),a==="click"){const y=(...A)=>{var H;const Y=A[0];if(!Y.coordinate)return;const[ut,dt]=Y.coordinate,Z=U(ut,dt);V(m,Z,h),(H=L.current)==null||H.call(L,Z)};E.current=y,m.on("singleclick",y)}},[i.status,a,e==null?void 0:e.lat,e==null?void 0:e.lon,t.lat,t.lon]),w.useImperativeHandle(f,()=>({getLuxMap:()=>o.current,setCenter(m){const l=o.current;if(!l)return;const{easting:y,northing:A}=C(m.lat,m.lon);l.getView().setCenter([y,A])},setZoom(m){var l;(l=o.current)==null||l.getView().setZoom(m)}})),i.status==="error"?R.jsx("div",{className:g,style:{display:"flex",alignItems:"center",justifyContent:"center",background:"#f5f5f5",...s},children:R.jsxs("span",{style:{color:"#c00"},children:["Failed to load Geoportail API: ",i.error.message]})}):R.jsxs("div",{style:{position:"relative",...s},className:g,children:[i.status==="loading"&&R.jsx("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(255,255,255,0.7)",zIndex:10},children:R.jsx("span",{children:"Loading map…"})}),R.jsx("div",{ref:d,style:{width:"100%",height:"100%"}})]})});function V(c,t,n){const u=window.ol;if(!u)return;const{easting:a,northing:e}=C(t.lat,t.lon);if(n.current){n.current.setPosition([a,e]);return}const p=document.createElement("div");p.innerHTML=rt,p.style.cssText="cursor:pointer;transform:translate(-50%,-100%);line-height:0;";const g=new u.Overlay({element:p,positioning:"bottom-center",stopEvent:!1});g.setPosition([a,e]),c.addOverlay(g),n.current=g}function ot(c,t){!t.current||!window.ol||(t.current.setPosition(void 0),t.current=null)}const rt=`<svg xmlns="http://www.w3.org/2000/svg" width="30" height="40" viewBox="0 0 30 40">
2
2
  <path d="M15 0C7.268 0 1 6.268 1 14c0 10.5 14 26 14 26S29 24.5 29 14C29 6.268 22.732 0 15 0z"
3
3
  fill="#e53935" stroke="#b71c1c" stroke-width="1.5"/>
4
4
  <circle cx="15" cy="14" r="5" fill="white"/>
5
- </svg>`,at="https://api.geoportail.lu/geocoder/reverseGeocode";function it(){const[c,t]=w.useState({status:"idle",address:null,error:null}),n=w.useRef(null);w.useEffect(()=>()=>{var e;(e=n.current)==null||e.abort()},[]);const l=w.useCallback(async e=>{var g,s;(g=n.current)==null||g.abort();const p=new AbortController;n.current=p,t({status:"loading",address:null,error:null});try{const{easting:r,northing:h}=C(e.lat,e.lon),f=new URL(at);f.searchParams.set("easting",String(r)),f.searchParams.set("northing",String(h));const u=await fetch(f.toString(),{signal:p.signal});if(!u.ok)throw new Error(`Reverse geocode request failed: ${u.status} ${u.statusText}`);const d=(s=(await u.json()).results)==null?void 0:s[0];if(!d)throw new Error("No results returned for this position");t({status:"success",address:{label:d.name??"",distance:d.distance??0,easting:d.easting,northing:d.northing},error:null})}catch(r){if(r instanceof DOMException&&r.name==="AbortError")return;t({status:"error",address:null,error:r instanceof Error?r:new Error(String(r))})}},[]),a=w.useCallback(()=>{var e;(e=n.current)==null||e.abort(),t({status:"idle",address:null,error:null})},[]);return{state:c,lookup:l,reset:a}}const lt="https://apiv3.geoportail.lu/geocode/search";function ut(){const[c,t]=w.useState({status:"idle",results:null,error:null}),n=w.useRef(null);w.useEffect(()=>()=>{var e;(e=n.current)==null||e.abort()},[]);const l=w.useCallback(async e=>{var g;(g=n.current)==null||g.abort();const p=new AbortController;n.current=p,t({status:"loading",results:null,error:null});try{const s=new URL(lt);e.queryString?s.searchParams.set("queryString",e.queryString):(e.num&&s.searchParams.set("num",e.num),e.street&&s.searchParams.set("street",e.street),e.zip&&s.searchParams.set("zip",e.zip),e.locality&&s.searchParams.set("locality",e.locality));const r=await fetch(s.toString(),{signal:p.signal});if(!r.ok)throw new Error(`Geocode request failed: ${r.status} ${r.statusText}`);const f=((await r.json()).results??[]).map(u=>{var o,d,M,L;return{latLon:j(u.easting,u.northing),easting:u.easting,northing:u.northing,accuracy:u.accuracy??0,street:(o=u.AddressDetails)==null?void 0:o.street,num:(d=u.AddressDetails)==null?void 0:d.number,zip:(M=u.AddressDetails)==null?void 0:M.zip,locality:(L=u.AddressDetails)==null?void 0:L.locality}});t({status:"success",results:f,error:null})}catch(s){if(s instanceof DOMException&&s.name==="AbortError")return;t({status:"error",results:null,error:s instanceof Error?s:new Error(String(s))})}},[]),a=w.useCallback(()=>{var e;(e=n.current)==null||e.abort(),t({status:"idle",results:null,error:null})},[]);return{state:c,search:l,reset:a}}x.GeoportailMap=ot,x.latLonToLuref=C,x.loadLuxApi=N,x.lurefToLatLon=j,x.useGeocode=ut,x.useLuxApi=X,x.useReverseGeocode=it,Object.defineProperty(x,Symbol.toStringTag,{value:"Module"})});
5
+ </svg>`,ct="https://api.geoportail.lu/geocoder/reverseGeocode";function at(){const[c,t]=w.useState({status:"idle",address:null,error:null}),n=w.useRef(null);w.useEffect(()=>()=>{var e;(e=n.current)==null||e.abort()},[]);const u=w.useCallback(async e=>{var g,s;(g=n.current)==null||g.abort();const p=new AbortController;n.current=p,t({status:"loading",address:null,error:null});try{const{easting:r,northing:f}=C(e.lat,e.lon),i=new URL(ct);i.searchParams.set("easting",String(r)),i.searchParams.set("northing",String(f));const d=await fetch(i.toString(),{signal:p.signal});if(!d.ok)throw new Error(`Reverse geocode request failed: ${d.status} ${d.statusText}`);const h=(s=(await d.json()).results)==null?void 0:s[0];if(!h)throw new Error("No results returned for this position");t({status:"success",address:{label:h.name??"",distance:h.distance??0,easting:h.easting,northing:h.northing},error:null})}catch(r){if(r instanceof DOMException&&r.name==="AbortError")return;t({status:"error",address:null,error:r instanceof Error?r:new Error(String(r))})}},[]),a=w.useCallback(()=>{var e;(e=n.current)==null||e.abort(),t({status:"idle",address:null,error:null})},[]);return{state:c,lookup:u,reset:a}}const it="https://apiv3.geoportail.lu/geocode/search";function lt(){const[c,t]=w.useState({status:"idle",results:null,error:null}),n=w.useRef(null);w.useEffect(()=>()=>{var e;(e=n.current)==null||e.abort()},[]);const u=w.useCallback(async e=>{var g;(g=n.current)==null||g.abort();const p=new AbortController;n.current=p,t({status:"loading",results:null,error:null});try{const s=new URL(it);e.queryString?s.searchParams.set("queryString",e.queryString):(e.num&&s.searchParams.set("num",e.num),e.street&&s.searchParams.set("street",e.street),e.zip&&s.searchParams.set("zip",e.zip),e.locality&&s.searchParams.set("locality",e.locality));const r=await fetch(s.toString(),{signal:p.signal});if(!r.ok)throw new Error(`Geocode request failed: ${r.status} ${r.statusText}`);const i=((await r.json()).results??[]).map(d=>{var o,h,E,L;return{latLon:U(d.easting,d.northing),easting:d.easting,northing:d.northing,accuracy:d.accuracy??0,street:(o=d.AddressDetails)==null?void 0:o.street,num:(h=d.AddressDetails)==null?void 0:h.number,zip:(E=d.AddressDetails)==null?void 0:E.zip,locality:(L=d.AddressDetails)==null?void 0:L.locality}});t({status:"success",results:i,error:null})}catch(s){if(s instanceof DOMException&&s.name==="AbortError")return;t({status:"error",results:null,error:s instanceof Error?s:new Error(String(s))})}},[]),a=w.useCallback(()=>{var e;(e=n.current)==null||e.abort(),t({status:"idle",results:null,error:null})},[]);return{state:c,search:u,reset:a}}b.GeoportailMap=st,b.latLonToLuref=C,b.loadLuxApi=z,b.lurefToLatLon=U,b.useGeocode=lt,b.useLuxApi=W,b.useReverseGeocode=at,Object.defineProperty(b,Symbol.toStringTag,{value:"Module"})});
6
6
  //# sourceMappingURL=react-geoportail.umd.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"react-geoportail.umd.cjs","sources":["../src/utils/loader.ts","../src/hooks/useLuxApi.ts","../src/utils/coordinates.ts","../src/components/GeoportailMap.tsx","../src/hooks/useReverseGeocode.ts","../src/hooks/useGeocode.ts"],"sourcesContent":["/**\n * Dynamically injects the Geoportail v3 API script and resolves when\n * the global `lux` namespace is available.\n */\n\nconst LUX_SCRIPT_URL = 'https://apiv3.geoportail.lu/apiv3loader.js';\nconst LUX_SCRIPT_ID = 'geoportail-apiv3-loader';\n\nlet loadPromise: Promise<void> | null = null;\n\n/**\n * Loads the Geoportail apiv3loader.js script exactly once.\n * Safe to call multiple times — subsequent calls return the same promise.\n */\nexport function loadLuxApi(): Promise<void> {\n if (loadPromise) return loadPromise;\n\n loadPromise = new Promise<void>((resolve, reject) => {\n // Already loaded (e.g. added manually in HTML)\n if (typeof window !== 'undefined' && window.lux) {\n resolve();\n return;\n }\n\n if (typeof document === 'undefined') {\n reject(new Error('loadLuxApi must run in a browser environment'));\n return;\n }\n\n // Avoid duplicate script tags\n if (document.getElementById(LUX_SCRIPT_ID)) {\n pollForLux(resolve, reject);\n return;\n }\n\n const script = document.createElement('script');\n script.id = LUX_SCRIPT_ID;\n script.src = LUX_SCRIPT_URL;\n script.async = true;\n\n script.onload = () => pollForLux(resolve, reject);\n script.onerror = () => {\n loadPromise = null; // allow retry\n reject(new Error(`Failed to load Geoportail API from ${LUX_SCRIPT_URL}`));\n };\n\n document.head.appendChild(script);\n });\n\n return loadPromise;\n}\n\n/**\n * The lux API may initialise asynchronously after the script loads.\n * Poll until window.lux is defined (max ~5 s).\n */\nfunction pollForLux(resolve: () => void, reject: (err: Error) => void): void {\n const maxAttempts = 100;\n let attempts = 0;\n\n const check = () => {\n if (window.lux) {\n resolve();\n return;\n }\n if (attempts++ >= maxAttempts) {\n loadPromise = null;\n reject(new Error('Timed out waiting for window.lux to be defined'));\n return;\n }\n setTimeout(check, 50);\n };\n\n check();\n}\n","import { useState, useEffect } from 'react';\nimport { loadLuxApi } from '../utils/loader.ts';\nimport type { LuxNamespace } from '../types/lux.d.ts';\n\nexport type LuxApiState =\n | { status: 'loading'; lux: null; error: null }\n | { status: 'ready'; lux: LuxNamespace; error: null }\n | { status: 'error'; lux: null; error: Error };\n\n/**\n * Loads the Geoportail apiv3 script and returns the `lux` namespace once ready.\n * Deduplicates the script load — safe to call from multiple components.\n */\nexport function useLuxApi(): LuxApiState {\n const [state, setState] = useState<LuxApiState>({\n status: 'loading',\n lux: null,\n error: null,\n });\n\n useEffect(() => {\n let cancelled = false;\n\n loadLuxApi()\n .then(() => {\n if (!cancelled) {\n setState({ status: 'ready', lux: window.lux!, error: null });\n }\n })\n .catch((err: unknown) => {\n if (!cancelled) {\n setState({\n status: 'error',\n lux: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, []);\n\n return state;\n}\n","/**\n * Coordinate conversion between EPSG:2169 (Luxembourg TM) and EPSG:4326 (WGS84).\n *\n * The Geoportail API operates internally in EPSG:2169 but also accepts\n * position arrays in EPSG:4326 via `lux.Map` constructor options.\n *\n * For precise server-side conversion the REST API itself is authoritative.\n * The formulas below use a 6-parameter Helmert + transverse Mercator approach\n * sufficient for Luxembourg's territory (~50 km radius).\n *\n * Reference: IGN-L / ACT official parameters for LUREF (EPSG:2169).\n */\n\n// LUREF / Luxembourg TM projection constants (EPSG:2169)\nconst FE = 80000; // False Easting (m)\nconst FN = 100000; // False Northing (m)\nconst LON0 = 6.16666666666667; // Central meridian (6°10') in degrees\nconst LAT0 = 49.8333333333333; // Latitude of origin (49°50') in degrees\nconst SCALE = 1.0; // Scale factor at central meridian\nconst A = 6378388.0; // Semi-major axis (Hayford / International 1924)\nconst F = 1 / 297.0; // Flattening\nconst B = A * (1 - F); // Semi-minor axis\n\nconst E2 = 1 - (B * B) / (A * A); // First eccentricity squared\n\n// Helmert 7-parameter shift: ETRS89 -> ED50 (approximate for Luxembourg)\n// These values transform WGS84 (≈ETRS89) to ED50 used by LUREF\nconst DX = -87.0;\nconst DY = -98.0;\nconst DZ = -121.0;\n\nfunction degToRad(d: number): number {\n return (d * Math.PI) / 180;\n}\n\nfunction radToDeg(r: number): number {\n return (r * 180) / Math.PI;\n}\n\n/**\n * Molodensky transformation: WGS84 -> ED50 (approximate, <1 m accuracy).\n */\nfunction wgs84ToEd50(\n lat: number,\n lon: number\n): { lat: number; lon: number } {\n const latR = degToRad(lat);\n const lonR = degToRad(lon);\n\n const sinLat = Math.sin(latR);\n const cosLat = Math.cos(latR);\n const sinLon = Math.sin(lonR);\n const cosLon = Math.cos(lonR);\n\n const aWgs = 6378137.0;\n const fWgs = 1 / 298.257223563;\n const bWgs = aWgs * (1 - fWgs);\n const e2Wgs = 1 - (bWgs * bWgs) / (aWgs * aWgs);\n\n const da = A - aWgs;\n const df = F - fWgs;\n\n const N = aWgs / Math.sqrt(1 - e2Wgs * sinLat * sinLat);\n const M =\n (aWgs * (1 - e2Wgs)) /\n Math.pow(1 - e2Wgs * sinLat * sinLat, 1.5);\n\n const dLat =\n (1 / M) *\n (N * (e2Wgs / (1 - e2Wgs)) * sinLat * cosLat * (da / aWgs) +\n (N / aWgs + 1) * sinLat * cosLat * df * (aWgs / bWgs) -\n (DX * sinLat * cosLon + DY * sinLat * sinLon - DZ * cosLat));\n\n const dLon =\n (1 / (N * cosLat)) * (-DX * sinLon + DY * cosLon);\n\n return {\n lat: lat + radToDeg(dLat),\n lon: lon + radToDeg(dLon),\n };\n}\n\n/**\n * ED50 -> WGS84 (inverse Molodensky, approximate).\n */\nfunction ed50ToWgs84(\n lat: number,\n lon: number\n): { lat: number; lon: number } {\n const latR = degToRad(lat);\n const lonR = degToRad(lon);\n\n const sinLat = Math.sin(latR);\n const cosLat = Math.cos(latR);\n const sinLon = Math.sin(lonR);\n const cosLon = Math.cos(lonR);\n\n const N = A / Math.sqrt(1 - E2 * sinLat * sinLat);\n const M = (A * (1 - E2)) / Math.pow(1 - E2 * sinLat * sinLat, 1.5);\n\n const da = 6378137.0 - A;\n const fWgs = 1 / 298.257223563;\n const df = fWgs - F;\n\n const dLat =\n (1 / M) *\n (N * (E2 / (1 - E2)) * sinLat * cosLat * (da / A) +\n (N / A + 1) * sinLat * cosLat * df * (A / B) +\n (DX * sinLat * cosLon + DY * sinLat * sinLon - DZ * cosLat));\n\n const dLon =\n (1 / (N * cosLat)) * (DX * sinLon - DY * cosLon);\n\n return {\n lat: lat + radToDeg(dLat),\n lon: lon + radToDeg(dLon),\n };\n}\n\n/**\n * Transverse Mercator forward projection (ED50 lat/lon -> LUREF easting/northing).\n */\nfunction tmForward(lat: number, lon: number): { easting: number; northing: number } {\n const latR = degToRad(lat);\n const lonR = degToRad(lon);\n const lon0R = degToRad(LON0);\n const lat0R = degToRad(LAT0);\n\n const sinLat = Math.sin(latR);\n const cosLat = Math.cos(latR);\n const tanLat = Math.tan(latR);\n\n const N = A / Math.sqrt(1 - E2 * sinLat * sinLat);\n const T = tanLat * tanLat;\n const C = (E2 / (1 - E2)) * cosLat * cosLat;\n const dl = lonR - lon0R;\n const XX = cosLat * dl;\n\n // Meridian arc from equator to lat\n const M = meridianArc(latR);\n const M0 = meridianArc(lat0R);\n\n const easting =\n FE +\n SCALE *\n N *\n (XX +\n (XX * XX * XX * (1 - T + C)) / 6 +\n (XX * XX * XX * XX * XX * (5 - 18 * T + T * T + 72 * C)) / 120);\n\n const northing =\n FN +\n SCALE *\n (M -\n M0 +\n N *\n tanLat *\n (XX * XX / 2 +\n (XX * XX * XX * XX * (5 - T + 9 * C + 4 * C * C)) / 24 +\n (XX * XX * XX * XX * XX * XX *\n (61 - 58 * T + T * T + 600 * C)) /\n 720));\n\n return { easting, northing };\n}\n\n/**\n * Transverse Mercator inverse projection (LUREF easting/northing -> ED50 lat/lon).\n */\nfunction tmInverse(easting: number, northing: number): { lat: number; lon: number } {\n const x = easting - FE;\n const y = northing - FN;\n const lat0R = degToRad(LAT0);\n const lon0R = degToRad(LON0);\n const M0 = meridianArc(lat0R);\n\n // Footpoint latitude\n const M1 = M0 + y / SCALE;\n const mu = M1 / (A * (1 - E2 / 4 - (3 * E2 * E2) / 64));\n\n const e1 = (1 - Math.sqrt(1 - E2)) / (1 + Math.sqrt(1 - E2));\n const phi1 =\n mu +\n ((3 * e1) / 2 - (27 * e1 * e1 * e1) / 32) * Math.sin(2 * mu) +\n ((21 * e1 * e1) / 16 - (55 * e1 * e1 * e1 * e1) / 32) *\n Math.sin(4 * mu) +\n ((151 * e1 * e1 * e1) / 96) * Math.sin(6 * mu);\n\n const sinPhi1 = Math.sin(phi1);\n const cosPhi1 = Math.cos(phi1);\n const tanPhi1 = Math.tan(phi1);\n\n const N1 = A / Math.sqrt(1 - E2 * sinPhi1 * sinPhi1);\n const T1 = tanPhi1 * tanPhi1;\n const C1 = (E2 / (1 - E2)) * cosPhi1 * cosPhi1;\n const R1 =\n (A * (1 - E2)) / Math.pow(1 - E2 * sinPhi1 * sinPhi1, 1.5);\n const D = x / (N1 * SCALE);\n\n const lat =\n phi1 -\n ((N1 * tanPhi1) / R1) *\n (D * D / 2 -\n (D * D * D * D * (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1)) / 24 +\n (D * D * D * D * D * D *\n (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1)) /\n 720);\n\n const lon =\n lon0R +\n (D -\n (D * D * D * (1 + 2 * T1 + C1)) / 6 +\n (D * D * D * D * D * (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1)) / 120) /\n cosPhi1;\n\n return { lat: radToDeg(lat), lon: radToDeg(lon) };\n}\n\nfunction meridianArc(latR: number): number {\n const e2 = E2;\n const e4 = e2 * e2;\n const e6 = e4 * e2;\n return (\n A *\n ((1 - e2 / 4 - (3 * e4) / 64 - (5 * e6) / 256) * latR -\n ((3 * e2) / 8 + (3 * e4) / 32 + (45 * e6) / 1024) *\n Math.sin(2 * latR) +\n ((15 * e4) / 256 + (45 * e6) / 1024) * Math.sin(4 * latR) -\n ((35 * e6) / 3072) * Math.sin(6 * latR))\n );\n}\n\n/**\n * Convert WGS84 (lat/lon) to EPSG:2169 (easting/northing).\n */\nexport function latLonToLuref(\n lat: number,\n lon: number\n): { easting: number; northing: number } {\n const ed50 = wgs84ToEd50(lat, lon);\n return tmForward(ed50.lat, ed50.lon);\n}\n\n/**\n * Convert EPSG:2169 (easting/northing) to WGS84 (lat/lon).\n */\nexport function lurefToLatLon(\n easting: number,\n northing: number\n): { lat: number; lon: number } {\n const ed50 = tmInverse(easting, northing);\n return ed50ToWgs84(ed50.lat, ed50.lon);\n}\n","import React, {\n useEffect,\n useRef,\n useId,\n forwardRef,\n useImperativeHandle,\n} from 'react';\nimport { useLuxApi } from '../hooks/useLuxApi.ts';\nimport { lurefToLatLon, latLonToLuref } from '../utils/coordinates.ts';\nimport type { LatLon, MapClickHandler, MarkerMode } from '../types/index.ts';\nimport type { LuxMapInstance } from '../types/lux.d.ts';\n\nexport interface GeoportailMapProps {\n /**\n * Initial center of the map.\n * Defaults to Luxembourg City (lat: 49.6116, lon: 6.1319).\n */\n center?: LatLon;\n\n /** Initial zoom level (1–20). Default: 12 */\n zoom?: number;\n\n /**\n * Background layer identifier.\n * Default: 'basemap_2015_global'\n */\n bgLayer?: string;\n\n /**\n * Controls pin/marker behaviour:\n * - 'none' — no marker\n * - 'fixed' — show a marker at `markerPosition` (does not move on click)\n * - 'click' — user clicks map to place/move the pin; fires `onMarkerPlace`\n *\n * Default: 'none'\n */\n markerMode?: MarkerMode;\n\n /**\n * Position of the marker when `markerMode` is 'fixed' or to pre-set an\n * initial pin when `markerMode` is 'click'.\n */\n markerPosition?: LatLon;\n\n /**\n * Called whenever the user places a pin (markerMode === 'click').\n * Receives the WGS84 lat/lon of the clicked point.\n */\n onMarkerPlace?: MapClickHandler;\n\n /** CSS class applied to the map container div */\n className?: string;\n\n /** Inline styles for the map container div */\n style?: React.CSSProperties;\n\n /** Additional numeric layer IDs to add on top of the background */\n layers?: number[];\n}\n\nexport interface GeoportailMapHandle {\n /** Returns the underlying lux.Map instance (or null before ready) */\n getLuxMap(): LuxMapInstance | null;\n /** Programmatically move the map center */\n setCenter(coords: LatLon): void;\n /** Programmatically set zoom */\n setZoom(zoom: number): void;\n}\n\nconst LUXEMBOURG_CITY: LatLon = { lat: 49.6116, lon: 6.1319 };\nconst DEFAULT_BG_LAYER = 'basemap_2015_global';\n\n/**\n * Renders a Geoportail Luxembourg map inside a React component.\n *\n * Expose a ref (`GeoportailMapHandle`) to access the underlying lux.Map instance\n * or imperatively control the view.\n */\nexport const GeoportailMap = forwardRef<GeoportailMapHandle, GeoportailMapProps>(\n function GeoportailMap(\n {\n center = LUXEMBOURG_CITY,\n zoom = 12,\n bgLayer = DEFAULT_BG_LAYER,\n markerMode = 'none',\n markerPosition,\n onMarkerPlace,\n className,\n style,\n layers,\n },\n ref\n ) {\n const generatedId = useId();\n // useId produces \":r0:\" style strings — strip colons for valid DOM id\n const mapId = `gp-map-${generatedId.replace(/:/g, '')}`;\n\n const luxApi = useLuxApi();\n const mapRef = useRef<LuxMapInstance | null>(null);\n const markerLayerRef = useRef<unknown>(null);\n const clickListenerRef = useRef<((...args: unknown[]) => void) | null>(null);\n\n // Keep stable refs to callbacks so effects don't re-run on every render\n const onMarkerPlaceRef = useRef(onMarkerPlace);\n onMarkerPlaceRef.current = onMarkerPlace;\n\n // ------------------------------------------------------------------ map init\n useEffect(() => {\n if (luxApi.status !== 'ready') return;\n\n const lux = luxApi.lux;\n const { easting, northing } = latLonToLuref(center.lat, center.lon);\n\n const mapInstance = new lux.Map({\n target: mapId,\n bgLayer,\n zoom,\n position: [easting, northing],\n ...(layers && layers.length > 0\n ? { layers, layerOpacities: layers.map(() => 1) }\n : {}),\n });\n\n mapRef.current = mapInstance;\n\n return () => {\n // lux.Map does not expose a destroy(); we clear our references\n mapRef.current = null;\n markerLayerRef.current = null;\n clickListenerRef.current = null;\n };\n // Only re-run when the API becomes ready or the map target changes.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [luxApi.status, mapId]);\n\n // ------------------------------------------------------------------ marker\n useEffect(() => {\n const map = mapRef.current;\n if (!map || luxApi.status !== 'ready') return;\n\n // Remove previous click listener\n if (clickListenerRef.current) {\n map.un('singleclick', clickListenerRef.current);\n clickListenerRef.current = null;\n }\n\n if (markerMode === 'none') {\n clearMarker(map, markerLayerRef);\n return;\n }\n\n const initialPos = markerPosition ?? (markerMode === 'fixed' ? center : undefined);\n if (initialPos) {\n placeMarker(map, initialPos, markerLayerRef);\n }\n\n if (markerMode === 'click') {\n const handler = (...args: unknown[]) => {\n // OpenLayers MapBrowserEvent — coordinate is in map projection (EPSG:2169)\n const evt = args[0] as { coordinate?: [number, number] };\n if (!evt.coordinate) return;\n\n const [e, n] = evt.coordinate;\n const latLon = lurefToLatLon(e, n);\n placeMarker(map, latLon, markerLayerRef);\n onMarkerPlaceRef.current?.(latLon);\n };\n\n clickListenerRef.current = handler;\n map.on('singleclick', handler);\n }\n }, [\n luxApi.status,\n markerMode,\n markerPosition?.lat,\n markerPosition?.lon,\n center.lat,\n center.lon,\n ]);\n\n // ------------------------------------------------------------------ imperative handle\n useImperativeHandle(ref, () => ({\n getLuxMap: () => mapRef.current,\n setCenter(coords: LatLon) {\n const map = mapRef.current;\n if (!map) return;\n const { easting, northing } = latLonToLuref(coords.lat, coords.lon);\n map.getView().setCenter([easting, northing]);\n },\n setZoom(z: number) {\n mapRef.current?.getView().setZoom(z);\n },\n }));\n\n // ------------------------------------------------------------------ render\n if (luxApi.status === 'error') {\n return (\n <div\n className={className}\n style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#f5f5f5', ...style }}\n >\n <span style={{ color: '#c00' }}>\n Failed to load Geoportail API: {luxApi.error.message}\n </span>\n </div>\n );\n }\n\n return (\n <div style={{ position: 'relative', ...style }} className={className}>\n {luxApi.status === 'loading' && (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'rgba(255,255,255,0.7)',\n zIndex: 10,\n }}\n >\n <span>Loading map…</span>\n </div>\n )}\n <div\n id={mapId}\n style={{ width: '100%', height: '100%' }}\n />\n </div>\n );\n }\n);\n\n// ------------------------------------------------------------------ helpers\n\n/**\n * Place/move an SVG pin marker on the map.\n * lux.Map is built on OpenLayers 3 — we use ol.Overlay for the marker.\n */\nfunction placeMarker(\n map: LuxMapInstance,\n position: LatLon,\n layerRef: React.MutableRefObject<unknown>\n): void {\n // Prefer ol (OpenLayers global) if available\n const ol = (window as unknown as { ol?: OlLike }).ol;\n if (!ol) return;\n\n const { easting, northing } = latLonToLuref(position.lat, position.lon);\n\n // Reuse existing overlay or create a new one\n if (layerRef.current) {\n const overlay = layerRef.current as OlOverlay;\n overlay.setPosition([easting, northing]);\n return;\n }\n\n const el = document.createElement('div');\n el.innerHTML = PIN_SVG;\n el.style.cssText =\n 'cursor:pointer;transform:translate(-50%,-100%);line-height:0;';\n\n const overlay = new ol.Overlay({\n element: el,\n positioning: 'bottom-center',\n stopEvent: false,\n });\n\n overlay.setPosition([easting, northing]);\n (map as unknown as { addOverlay(o: OlOverlay): void }).addOverlay(overlay);\n layerRef.current = overlay;\n}\n\nfunction clearMarker(\n _map: LuxMapInstance,\n layerRef: React.MutableRefObject<unknown>\n): void {\n if (!layerRef.current) return;\n const ol = (window as unknown as { ol?: OlLike }).ol;\n if (!ol) return;\n // Overlay doesn't have a built-in remove on the overlay itself;\n // set position to undefined to hide it\n (layerRef.current as OlOverlay).setPosition(undefined);\n layerRef.current = null;\n}\n\n// Minimal pin SVG (red teardrop, 30×40)\nconst PIN_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"30\" height=\"40\" viewBox=\"0 0 30 40\">\n <path d=\"M15 0C7.268 0 1 6.268 1 14c0 10.5 14 26 14 26S29 24.5 29 14C29 6.268 22.732 0 15 0z\"\n fill=\"#e53935\" stroke=\"#b71c1c\" stroke-width=\"1.5\"/>\n <circle cx=\"15\" cy=\"14\" r=\"5\" fill=\"white\"/>\n</svg>`;\n\n// Minimal OL type stubs used only within this file\ninterface OlOverlay {\n setPosition(pos: [number, number] | undefined): void;\n}\n\ninterface OlLike {\n Overlay: new (opts: { element: HTMLElement; positioning: string; stopEvent: boolean }) => OlOverlay;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { latLonToLuref } from '../utils/coordinates.ts';\nimport type { LatLon, Address } from '../types/index.ts';\n\nconst REVERSE_GEOCODE_URL = 'https://api.geoportail.lu/geocoder/reverseGeocode';\n\nexport type ReverseGeocodeState =\n | { status: 'idle'; address: null; error: null }\n | { status: 'loading'; address: null; error: null }\n | { status: 'success'; address: Address; error: null }\n | { status: 'error'; address: null; error: Error };\n\n/**\n * Hook for reverse geocoding: convert a WGS84 lat/lon to a Luxembourg address.\n *\n * Uses the Geoportail REST reverse geocode endpoint — no API key required.\n *\n * @example\n * const { state, lookup } = useReverseGeocode();\n * // ...\n * lookup({ lat: 49.6116, lon: 6.1319 });\n * if (state.status === 'success') console.log(state.address.label);\n */\nexport function useReverseGeocode() {\n const [state, setState] = useState<ReverseGeocodeState>({\n status: 'idle',\n address: null,\n error: null,\n });\n\n const abortRef = useRef<AbortController | null>(null);\n\n useEffect(() => () => { abortRef.current?.abort(); }, []);\n\n const lookup = useCallback(async (position: LatLon) => {\n // Cancel any in-flight request\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setState({ status: 'loading', address: null, error: null });\n\n try {\n const { easting, northing } = latLonToLuref(position.lat, position.lon);\n\n const url = new URL(REVERSE_GEOCODE_URL);\n url.searchParams.set('easting', String(easting));\n url.searchParams.set('northing', String(northing));\n\n const res = await fetch(url.toString(), { signal: controller.signal });\n\n if (!res.ok) {\n throw new Error(`Reverse geocode request failed: ${res.status} ${res.statusText}`);\n }\n\n const data = (await res.json()) as {\n results?: Array<{\n easting: number;\n northing: number;\n name?: string;\n distance?: number;\n }>;\n };\n\n const first = data.results?.[0];\n if (!first) {\n throw new Error('No results returned for this position');\n }\n\n setState({\n status: 'success',\n address: {\n label: first.name ?? '',\n distance: first.distance ?? 0,\n easting: first.easting,\n northing: first.northing,\n },\n error: null,\n });\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n setState({\n status: 'error',\n address: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState({ status: 'idle', address: null, error: null });\n }, []);\n\n return { state, lookup, reset };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { lurefToLatLon } from '../utils/coordinates.ts';\nimport type { GeocodeQuery, LatLon } from '../types/index.ts';\n\nconst GEOCODE_URL = 'https://apiv3.geoportail.lu/geocode/search';\n\nexport interface GeocodeResultItem {\n latLon: LatLon;\n easting: number;\n northing: number;\n accuracy: number;\n street?: string;\n num?: string;\n zip?: string;\n locality?: string;\n}\n\nexport type GeocodeState =\n | { status: 'idle'; results: null; error: null }\n | { status: 'loading'; results: null; error: null }\n | { status: 'success'; results: GeocodeResultItem[]; error: null }\n | { status: 'error'; results: null; error: Error };\n\n/**\n * Hook for forward geocoding: search for a Luxembourg address and get coordinates.\n *\n * @example\n * const { state, search } = useGeocode();\n * search({ queryString: '1 rue du Fort Thüngen, Luxembourg' });\n * if (state.status === 'success') {\n * const { lat, lon } = state.results[0].latLon;\n * }\n */\nexport function useGeocode() {\n const [state, setState] = useState<GeocodeState>({\n status: 'idle',\n results: null,\n error: null,\n });\n\n const abortRef = useRef<AbortController | null>(null);\n\n useEffect(() => () => { abortRef.current?.abort(); }, []);\n\n const search = useCallback(async (query: GeocodeQuery) => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setState({ status: 'loading', results: null, error: null });\n\n try {\n const url = new URL(GEOCODE_URL);\n\n if (query.queryString) {\n url.searchParams.set('queryString', query.queryString);\n } else {\n if (query.num) url.searchParams.set('num', query.num);\n if (query.street) url.searchParams.set('street', query.street);\n if (query.zip) url.searchParams.set('zip', query.zip);\n if (query.locality) url.searchParams.set('locality', query.locality);\n }\n\n const res = await fetch(url.toString(), { signal: controller.signal });\n\n if (!res.ok) {\n throw new Error(`Geocode request failed: ${res.status} ${res.statusText}`);\n }\n\n const data = (await res.json()) as {\n results?: Array<{\n easting: number;\n northing: number;\n accuracy?: number;\n AddressDetails?: {\n street?: string;\n number?: string;\n zip?: string;\n locality?: string;\n };\n }>;\n };\n\n const results: GeocodeResultItem[] = (data.results ?? []).map((r) => ({\n latLon: lurefToLatLon(r.easting, r.northing),\n easting: r.easting,\n northing: r.northing,\n accuracy: r.accuracy ?? 0,\n street: r.AddressDetails?.street,\n num: r.AddressDetails?.number,\n zip: r.AddressDetails?.zip,\n locality: r.AddressDetails?.locality,\n }));\n\n setState({ status: 'success', results, error: null });\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n setState({\n status: 'error',\n results: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState({ status: 'idle', results: null, error: null });\n }, []);\n\n return { state, search, reset };\n}\n"],"names":["LUX_SCRIPT_URL","LUX_SCRIPT_ID","loadPromise","loadLuxApi","resolve","reject","pollForLux","script","attempts","check","useLuxApi","state","setState","useState","useEffect","cancelled","err","FE","FN","LON0","LAT0","SCALE","A","F","B","E2","DX","DY","DZ","degToRad","d","radToDeg","r","wgs84ToEd50","lat","lon","latR","lonR","sinLat","cosLat","sinLon","cosLon","aWgs","fWgs","bWgs","e2Wgs","da","df","N","dLat","dLon","ed50ToWgs84","M","tmForward","lon0R","lat0R","tanLat","T","C","dl","XX","meridianArc","M0","easting","northing","tmInverse","x","y","mu","e1","phi1","sinPhi1","cosPhi1","tanPhi1","N1","T1","C1","R1","D","e2","e4","e6","latLonToLuref","ed50","lurefToLatLon","LUXEMBOURG_CITY","DEFAULT_BG_LAYER","GeoportailMap","forwardRef","center","zoom","bgLayer","markerMode","markerPosition","onMarkerPlace","className","style","layers","ref","mapId","useId","luxApi","mapRef","useRef","markerLayerRef","clickListenerRef","onMarkerPlaceRef","lux","mapInstance","map","clearMarker","initialPos","placeMarker","handler","args","evt","e","n","latLon","_a","useImperativeHandle","coords","z","jsx","jsxs","position","layerRef","ol","el","PIN_SVG","overlay","_map","REVERSE_GEOCODE_URL","useReverseGeocode","abortRef","lookup","useCallback","controller","url","res","first","_b","reset","GEOCODE_URL","useGeocode","search","query","results","_c","_d"],"mappings":"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"}
1
+ {"version":3,"file":"react-geoportail.umd.cjs","sources":["../src/utils/loader.ts","../src/hooks/useLuxApi.ts","../src/utils/coordinates.ts","../src/components/GeoportailMap.tsx","../src/hooks/useReverseGeocode.ts","../src/hooks/useGeocode.ts"],"sourcesContent":["/**\n * Dynamically injects the Geoportail v3 API script and resolves when\n * the global `lux` namespace is available.\n */\n\nconst LUX_SCRIPT_URL = 'https://apiv3.geoportail.lu/apiv3loader.js';\nconst LUX_SCRIPT_ID = 'geoportail-apiv3-loader';\n\nlet loadPromise: Promise<void> | null = null;\n\n/**\n * Loads the Geoportail apiv3loader.js script exactly once.\n * Safe to call multiple times — subsequent calls return the same promise.\n */\nexport function loadLuxApi(): Promise<void> {\n if (loadPromise) return loadPromise;\n\n loadPromise = new Promise<void>((resolve, reject) => {\n // Already loaded (e.g. added manually in HTML)\n if (typeof window !== 'undefined' && window.lux) {\n resolve();\n return;\n }\n\n if (typeof document === 'undefined') {\n reject(new Error('loadLuxApi must run in a browser environment'));\n return;\n }\n\n // Avoid duplicate script tags\n if (document.getElementById(LUX_SCRIPT_ID)) {\n pollForLux(resolve, reject);\n return;\n }\n\n const script = document.createElement('script');\n script.id = LUX_SCRIPT_ID;\n script.src = LUX_SCRIPT_URL;\n script.async = true;\n\n script.onload = () => pollForLux(resolve, reject);\n script.onerror = () => {\n loadPromise = null; // allow retry\n reject(new Error(`Failed to load Geoportail API from ${LUX_SCRIPT_URL}`));\n };\n\n document.head.appendChild(script);\n });\n\n return loadPromise;\n}\n\n/**\n * The lux API may initialise asynchronously after the script loads.\n * Poll until window.lux is defined (max ~5 s).\n */\nfunction pollForLux(resolve: () => void, reject: (err: Error) => void): void {\n const maxAttempts = 100;\n let attempts = 0;\n\n const check = () => {\n if (window.lux) {\n resolve();\n return;\n }\n if (attempts++ >= maxAttempts) {\n loadPromise = null;\n reject(new Error('Timed out waiting for window.lux to be defined'));\n return;\n }\n setTimeout(check, 50);\n };\n\n check();\n}\n","import { useState, useEffect } from 'react';\nimport { loadLuxApi } from '../utils/loader';\nimport type { LuxNamespace } from '../types/lux';\n\nexport type LuxApiState =\n | { status: 'loading'; lux: null; error: null }\n | { status: 'ready'; lux: LuxNamespace; error: null }\n | { status: 'error'; lux: null; error: Error };\n\n/**\n * Loads the Geoportail apiv3 script and returns the `lux` namespace once ready.\n * Deduplicates the script load — safe to call from multiple components.\n */\nexport function useLuxApi(): LuxApiState {\n const [state, setState] = useState<LuxApiState>({\n status: 'loading',\n lux: null,\n error: null,\n });\n\n useEffect(() => {\n let cancelled = false;\n\n loadLuxApi()\n .then(() => {\n if (!cancelled) {\n setState({ status: 'ready', lux: window.lux!, error: null });\n }\n })\n .catch((err: unknown) => {\n if (!cancelled) {\n setState({\n status: 'error',\n lux: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, []);\n\n return state;\n}\n","/**\n * Coordinate conversion between EPSG:2169 (Luxembourg TM) and EPSG:4326 (WGS84).\n *\n * The Geoportail API operates internally in EPSG:2169 but also accepts\n * position arrays in EPSG:4326 via `lux.Map` constructor options.\n *\n * For precise server-side conversion the REST API itself is authoritative.\n * The formulas below use a 6-parameter Helmert + transverse Mercator approach\n * sufficient for Luxembourg's territory (~50 km radius).\n *\n * Reference: IGN-L / ACT official parameters for LUREF (EPSG:2169).\n */\n\n// LUREF / Luxembourg TM projection constants (EPSG:2169)\nconst FE = 80000; // False Easting (m)\nconst FN = 100000; // False Northing (m)\nconst LON0 = 6.16666666666667; // Central meridian (6°10') in degrees\nconst LAT0 = 49.8333333333333; // Latitude of origin (49°50') in degrees\nconst SCALE = 1.0; // Scale factor at central meridian\nconst A = 6378388.0; // Semi-major axis (Hayford / International 1924)\nconst F = 1 / 297.0; // Flattening\nconst B = A * (1 - F); // Semi-minor axis\n\nconst E2 = 1 - (B * B) / (A * A); // First eccentricity squared\n\n// Helmert 7-parameter shift: ETRS89 -> ED50 (approximate for Luxembourg)\n// These values transform WGS84 (≈ETRS89) to ED50 used by LUREF\nconst DX = -87.0;\nconst DY = -98.0;\nconst DZ = -121.0;\n\nfunction degToRad(d: number): number {\n return (d * Math.PI) / 180;\n}\n\nfunction radToDeg(r: number): number {\n return (r * 180) / Math.PI;\n}\n\n/**\n * Molodensky transformation: WGS84 -> ED50 (approximate, <1 m accuracy).\n */\nfunction wgs84ToEd50(\n lat: number,\n lon: number\n): { lat: number; lon: number } {\n const latR = degToRad(lat);\n const lonR = degToRad(lon);\n\n const sinLat = Math.sin(latR);\n const cosLat = Math.cos(latR);\n const sinLon = Math.sin(lonR);\n const cosLon = Math.cos(lonR);\n\n const aWgs = 6378137.0;\n const fWgs = 1 / 298.257223563;\n const bWgs = aWgs * (1 - fWgs);\n const e2Wgs = 1 - (bWgs * bWgs) / (aWgs * aWgs);\n\n const da = A - aWgs;\n const df = F - fWgs;\n\n const N = aWgs / Math.sqrt(1 - e2Wgs * sinLat * sinLat);\n const M =\n (aWgs * (1 - e2Wgs)) /\n Math.pow(1 - e2Wgs * sinLat * sinLat, 1.5);\n\n const dLat =\n (1 / M) *\n (N * (e2Wgs / (1 - e2Wgs)) * sinLat * cosLat * (da / aWgs) +\n (N / aWgs + 1) * sinLat * cosLat * df * (aWgs / bWgs) -\n (DX * sinLat * cosLon + DY * sinLat * sinLon - DZ * cosLat));\n\n const dLon =\n (1 / (N * cosLat)) * (-DX * sinLon + DY * cosLon);\n\n return {\n lat: lat + radToDeg(dLat),\n lon: lon + radToDeg(dLon),\n };\n}\n\n/**\n * ED50 -> WGS84 (inverse Molodensky, approximate).\n */\nfunction ed50ToWgs84(\n lat: number,\n lon: number\n): { lat: number; lon: number } {\n const latR = degToRad(lat);\n const lonR = degToRad(lon);\n\n const sinLat = Math.sin(latR);\n const cosLat = Math.cos(latR);\n const sinLon = Math.sin(lonR);\n const cosLon = Math.cos(lonR);\n\n const N = A / Math.sqrt(1 - E2 * sinLat * sinLat);\n const M = (A * (1 - E2)) / Math.pow(1 - E2 * sinLat * sinLat, 1.5);\n\n const da = 6378137.0 - A;\n const fWgs = 1 / 298.257223563;\n const df = fWgs - F;\n\n const dLat =\n (1 / M) *\n (N * (E2 / (1 - E2)) * sinLat * cosLat * (da / A) +\n (N / A + 1) * sinLat * cosLat * df * (A / B) +\n (DX * sinLat * cosLon + DY * sinLat * sinLon - DZ * cosLat));\n\n const dLon =\n (1 / (N * cosLat)) * (DX * sinLon - DY * cosLon);\n\n return {\n lat: lat + radToDeg(dLat),\n lon: lon + radToDeg(dLon),\n };\n}\n\n/**\n * Transverse Mercator forward projection (ED50 lat/lon -> LUREF easting/northing).\n */\nfunction tmForward(lat: number, lon: number): { easting: number; northing: number } {\n const latR = degToRad(lat);\n const lonR = degToRad(lon);\n const lon0R = degToRad(LON0);\n const lat0R = degToRad(LAT0);\n\n const sinLat = Math.sin(latR);\n const cosLat = Math.cos(latR);\n const tanLat = Math.tan(latR);\n\n const N = A / Math.sqrt(1 - E2 * sinLat * sinLat);\n const T = tanLat * tanLat;\n const C = (E2 / (1 - E2)) * cosLat * cosLat;\n const dl = lonR - lon0R;\n const XX = cosLat * dl;\n\n // Meridian arc from equator to lat\n const M = meridianArc(latR);\n const M0 = meridianArc(lat0R);\n\n const easting =\n FE +\n SCALE *\n N *\n (XX +\n (XX * XX * XX * (1 - T + C)) / 6 +\n (XX * XX * XX * XX * XX * (5 - 18 * T + T * T + 72 * C)) / 120);\n\n const northing =\n FN +\n SCALE *\n (M -\n M0 +\n N *\n tanLat *\n (XX * XX / 2 +\n (XX * XX * XX * XX * (5 - T + 9 * C + 4 * C * C)) / 24 +\n (XX * XX * XX * XX * XX * XX *\n (61 - 58 * T + T * T + 600 * C)) /\n 720));\n\n return { easting, northing };\n}\n\n/**\n * Transverse Mercator inverse projection (LUREF easting/northing -> ED50 lat/lon).\n */\nfunction tmInverse(easting: number, northing: number): { lat: number; lon: number } {\n const x = easting - FE;\n const y = northing - FN;\n const lat0R = degToRad(LAT0);\n const lon0R = degToRad(LON0);\n const M0 = meridianArc(lat0R);\n\n // Footpoint latitude\n const M1 = M0 + y / SCALE;\n const mu = M1 / (A * (1 - E2 / 4 - (3 * E2 * E2) / 64));\n\n const e1 = (1 - Math.sqrt(1 - E2)) / (1 + Math.sqrt(1 - E2));\n const phi1 =\n mu +\n ((3 * e1) / 2 - (27 * e1 * e1 * e1) / 32) * Math.sin(2 * mu) +\n ((21 * e1 * e1) / 16 - (55 * e1 * e1 * e1 * e1) / 32) *\n Math.sin(4 * mu) +\n ((151 * e1 * e1 * e1) / 96) * Math.sin(6 * mu);\n\n const sinPhi1 = Math.sin(phi1);\n const cosPhi1 = Math.cos(phi1);\n const tanPhi1 = Math.tan(phi1);\n\n const N1 = A / Math.sqrt(1 - E2 * sinPhi1 * sinPhi1);\n const T1 = tanPhi1 * tanPhi1;\n const C1 = (E2 / (1 - E2)) * cosPhi1 * cosPhi1;\n const R1 =\n (A * (1 - E2)) / Math.pow(1 - E2 * sinPhi1 * sinPhi1, 1.5);\n const D = x / (N1 * SCALE);\n\n const lat =\n phi1 -\n ((N1 * tanPhi1) / R1) *\n (D * D / 2 -\n (D * D * D * D * (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1)) / 24 +\n (D * D * D * D * D * D *\n (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1)) /\n 720);\n\n const lon =\n lon0R +\n (D -\n (D * D * D * (1 + 2 * T1 + C1)) / 6 +\n (D * D * D * D * D * (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1)) / 120) /\n cosPhi1;\n\n return { lat: radToDeg(lat), lon: radToDeg(lon) };\n}\n\nfunction meridianArc(latR: number): number {\n const e2 = E2;\n const e4 = e2 * e2;\n const e6 = e4 * e2;\n return (\n A *\n ((1 - e2 / 4 - (3 * e4) / 64 - (5 * e6) / 256) * latR -\n ((3 * e2) / 8 + (3 * e4) / 32 + (45 * e6) / 1024) *\n Math.sin(2 * latR) +\n ((15 * e4) / 256 + (45 * e6) / 1024) * Math.sin(4 * latR) -\n ((35 * e6) / 3072) * Math.sin(6 * latR))\n );\n}\n\n/**\n * Convert WGS84 (lat/lon) to EPSG:2169 (easting/northing).\n */\nexport function latLonToLuref(\n lat: number,\n lon: number\n): { easting: number; northing: number } {\n const ed50 = wgs84ToEd50(lat, lon);\n return tmForward(ed50.lat, ed50.lon);\n}\n\n/**\n * Convert EPSG:2169 (easting/northing) to WGS84 (lat/lon).\n */\nexport function lurefToLatLon(\n easting: number,\n northing: number\n): { lat: number; lon: number } {\n const ed50 = tmInverse(easting, northing);\n return ed50ToWgs84(ed50.lat, ed50.lon);\n}\n","import React, {\n useEffect,\n useRef,\n forwardRef,\n useImperativeHandle,\n} from 'react';\nimport { useLuxApi } from '../hooks/useLuxApi';\nimport { lurefToLatLon, latLonToLuref } from '../utils/coordinates';\nimport type { LatLon, MapClickHandler, MarkerMode } from '../types';\nimport type { LuxMapInstance } from '../types/lux';\n\nexport interface GeoportailMapProps {\n /**\n * Initial center of the map.\n * Defaults to Luxembourg City (lat: 49.6116, lon: 6.1319).\n */\n center?: LatLon;\n\n /** Initial zoom level (1–20). Default: 12 */\n zoom?: number;\n\n /**\n * Background layer identifier.\n * Default: 'basemap_2015_global'\n */\n bgLayer?: string;\n\n /**\n * Controls pin/marker behaviour:\n * - 'none' — no marker\n * - 'fixed' — show a marker at `markerPosition` (does not move on click)\n * - 'click' — user clicks map to place/move the pin; fires `onMarkerPlace`\n *\n * Default: 'none'\n */\n markerMode?: MarkerMode;\n\n /**\n * Position of the marker when `markerMode` is 'fixed' or to pre-set an\n * initial pin when `markerMode` is 'click'.\n */\n markerPosition?: LatLon;\n\n /**\n * Called whenever the user places a pin (markerMode === 'click').\n * Receives the WGS84 lat/lon of the clicked point.\n */\n onMarkerPlace?: MapClickHandler;\n\n /** CSS class applied to the map container div */\n className?: string;\n\n /** Inline styles for the map container div */\n style?: React.CSSProperties;\n\n /** Additional numeric layer IDs to add on top of the background */\n layers?: number[];\n}\n\nexport interface GeoportailMapHandle {\n /** Returns the underlying lux.Map instance (or null before ready) */\n getLuxMap(): LuxMapInstance | null;\n /** Programmatically move the map center */\n setCenter(coords: LatLon): void;\n /** Programmatically set zoom */\n setZoom(zoom: number): void;\n}\n\nconst LUXEMBOURG_CITY: LatLon = { lat: 49.6116, lon: 6.1319 };\nconst DEFAULT_BG_LAYER = 'basemap_2015_global';\n\n/**\n * Renders a Geoportail Luxembourg map inside a React component.\n *\n * Expose a ref (`GeoportailMapHandle`) to access the underlying lux.Map instance\n * or imperatively control the view.\n */\nexport const GeoportailMap = forwardRef<GeoportailMapHandle, GeoportailMapProps>(\n function GeoportailMap(\n {\n center = LUXEMBOURG_CITY,\n zoom = 12,\n bgLayer = DEFAULT_BG_LAYER,\n markerMode = 'none',\n markerPosition,\n onMarkerPlace,\n className,\n style,\n layers,\n },\n ref\n ) {\n const luxApi = useLuxApi();\n const mapContainerRef = useRef<HTMLDivElement | null>(null);\n const mapRef = useRef<LuxMapInstance | null>(null);\n const markerLayerRef = useRef<unknown>(null);\n const clickListenerRef = useRef<((...args: unknown[]) => void) | null>(null);\n\n // Keep stable refs to callbacks so effects don't re-run on every render\n const onMarkerPlaceRef = useRef(onMarkerPlace);\n onMarkerPlaceRef.current = onMarkerPlace;\n\n // ------------------------------------------------------------------ map init\n useEffect(() => {\n if (luxApi.status !== 'ready') return;\n if (!mapContainerRef.current) return;\n\n const lux = luxApi.lux;\n const { easting, northing } = latLonToLuref(center.lat, center.lon);\n\n const mapInstance = new lux.Map({\n target: mapContainerRef.current,\n bgLayer,\n zoom,\n position: [easting, northing],\n ...(layers && layers.length > 0\n ? { layers, layerOpacities: layers.map(() => 1) }\n : {}),\n });\n\n mapRef.current = mapInstance;\n\n return () => {\n // lux.Map does not expose a destroy(); we clear our references\n mapRef.current = null;\n markerLayerRef.current = null;\n clickListenerRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [luxApi.status]);\n\n // ------------------------------------------------------------------ marker\n useEffect(() => {\n const map = mapRef.current;\n if (!map || luxApi.status !== 'ready') return;\n\n // Remove previous click listener\n if (clickListenerRef.current) {\n map.un('singleclick', clickListenerRef.current);\n clickListenerRef.current = null;\n }\n\n if (markerMode === 'none') {\n clearMarker(map, markerLayerRef);\n return;\n }\n\n const initialPos = markerPosition ?? (markerMode === 'fixed' ? center : undefined);\n if (initialPos) {\n placeMarker(map, initialPos, markerLayerRef);\n }\n\n if (markerMode === 'click') {\n const handler = (...args: unknown[]) => {\n // OpenLayers MapBrowserEvent — coordinate is in map projection (EPSG:2169)\n const evt = args[0] as { coordinate?: [number, number] };\n if (!evt.coordinate) return;\n\n const [e, n] = evt.coordinate;\n const latLon = lurefToLatLon(e, n);\n placeMarker(map, latLon, markerLayerRef);\n onMarkerPlaceRef.current?.(latLon);\n };\n\n clickListenerRef.current = handler;\n map.on('singleclick', handler);\n }\n }, [\n luxApi.status,\n markerMode,\n markerPosition?.lat,\n markerPosition?.lon,\n center.lat,\n center.lon,\n ]);\n\n // ------------------------------------------------------------------ imperative handle\n useImperativeHandle(ref, () => ({\n getLuxMap: () => mapRef.current,\n setCenter(coords: LatLon) {\n const map = mapRef.current;\n if (!map) return;\n const { easting, northing } = latLonToLuref(coords.lat, coords.lon);\n map.getView().setCenter([easting, northing]);\n },\n setZoom(z: number) {\n mapRef.current?.getView().setZoom(z);\n },\n }));\n\n // ------------------------------------------------------------------ render\n if (luxApi.status === 'error') {\n return (\n <div\n className={className}\n style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', background: '#f5f5f5', ...style }}\n >\n <span style={{ color: '#c00' }}>\n Failed to load Geoportail API: {luxApi.error.message}\n </span>\n </div>\n );\n }\n\n return (\n <div style={{ position: 'relative', ...style }} className={className}>\n {luxApi.status === 'loading' && (\n <div\n style={{\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'rgba(255,255,255,0.7)',\n zIndex: 10,\n }}\n >\n <span>Loading map…</span>\n </div>\n )}\n <div\n ref={mapContainerRef}\n style={{ width: '100%', height: '100%' }}\n />\n </div>\n );\n }\n);\n\n// ------------------------------------------------------------------ helpers\n\n/**\n * Place/move an SVG pin marker on the map.\n * lux.Map is built on OpenLayers 3 — we use ol.Overlay for the marker.\n */\nfunction placeMarker(\n map: LuxMapInstance,\n position: LatLon,\n layerRef: React.MutableRefObject<unknown>\n): void {\n // Prefer ol (OpenLayers global) if available\n const ol = (window as unknown as { ol?: OlLike }).ol;\n if (!ol) return;\n\n const { easting, northing } = latLonToLuref(position.lat, position.lon);\n\n // Reuse existing overlay or create a new one\n if (layerRef.current) {\n const overlay = layerRef.current as OlOverlay;\n overlay.setPosition([easting, northing]);\n return;\n }\n\n const el = document.createElement('div');\n el.innerHTML = PIN_SVG;\n el.style.cssText =\n 'cursor:pointer;transform:translate(-50%,-100%);line-height:0;';\n\n const overlay = new ol.Overlay({\n element: el,\n positioning: 'bottom-center',\n stopEvent: false,\n });\n\n overlay.setPosition([easting, northing]);\n (map as unknown as { addOverlay(o: OlOverlay): void }).addOverlay(overlay);\n layerRef.current = overlay;\n}\n\nfunction clearMarker(\n _map: LuxMapInstance,\n layerRef: React.MutableRefObject<unknown>\n): void {\n if (!layerRef.current) return;\n const ol = (window as unknown as { ol?: OlLike }).ol;\n if (!ol) return;\n // Overlay doesn't have a built-in remove on the overlay itself;\n // set position to undefined to hide it\n (layerRef.current as OlOverlay).setPosition(undefined);\n layerRef.current = null;\n}\n\n// Minimal pin SVG (red teardrop, 30×40)\nconst PIN_SVG = `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"30\" height=\"40\" viewBox=\"0 0 30 40\">\n <path d=\"M15 0C7.268 0 1 6.268 1 14c0 10.5 14 26 14 26S29 24.5 29 14C29 6.268 22.732 0 15 0z\"\n fill=\"#e53935\" stroke=\"#b71c1c\" stroke-width=\"1.5\"/>\n <circle cx=\"15\" cy=\"14\" r=\"5\" fill=\"white\"/>\n</svg>`;\n\n// Minimal OL type stubs used only within this file\ninterface OlOverlay {\n setPosition(pos: [number, number] | undefined): void;\n}\n\ninterface OlLike {\n Overlay: new (opts: { element: HTMLElement; positioning: string; stopEvent: boolean }) => OlOverlay;\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { latLonToLuref } from '../utils/coordinates';\nimport type { LatLon, Address } from '../types';\n\nconst REVERSE_GEOCODE_URL = 'https://api.geoportail.lu/geocoder/reverseGeocode';\n\nexport type ReverseGeocodeState =\n | { status: 'idle'; address: null; error: null }\n | { status: 'loading'; address: null; error: null }\n | { status: 'success'; address: Address; error: null }\n | { status: 'error'; address: null; error: Error };\n\n/**\n * Hook for reverse geocoding: convert a WGS84 lat/lon to a Luxembourg address.\n *\n * Uses the Geoportail REST reverse geocode endpoint — no API key required.\n *\n * @example\n * const { state, lookup } = useReverseGeocode();\n * // ...\n * lookup({ lat: 49.6116, lon: 6.1319 });\n * if (state.status === 'success') console.log(state.address.label);\n */\nexport function useReverseGeocode() {\n const [state, setState] = useState<ReverseGeocodeState>({\n status: 'idle',\n address: null,\n error: null,\n });\n\n const abortRef = useRef<AbortController | null>(null);\n\n useEffect(() => () => { abortRef.current?.abort(); }, []);\n\n const lookup = useCallback(async (position: LatLon) => {\n // Cancel any in-flight request\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setState({ status: 'loading', address: null, error: null });\n\n try {\n const { easting, northing } = latLonToLuref(position.lat, position.lon);\n\n const url = new URL(REVERSE_GEOCODE_URL);\n url.searchParams.set('easting', String(easting));\n url.searchParams.set('northing', String(northing));\n\n const res = await fetch(url.toString(), { signal: controller.signal });\n\n if (!res.ok) {\n throw new Error(`Reverse geocode request failed: ${res.status} ${res.statusText}`);\n }\n\n const data = (await res.json()) as {\n results?: Array<{\n easting: number;\n northing: number;\n name?: string;\n distance?: number;\n }>;\n };\n\n const first = data.results?.[0];\n if (!first) {\n throw new Error('No results returned for this position');\n }\n\n setState({\n status: 'success',\n address: {\n label: first.name ?? '',\n distance: first.distance ?? 0,\n easting: first.easting,\n northing: first.northing,\n },\n error: null,\n });\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n setState({\n status: 'error',\n address: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState({ status: 'idle', address: null, error: null });\n }, []);\n\n return { state, lookup, reset };\n}\n","import { useState, useEffect, useCallback, useRef } from 'react';\nimport { lurefToLatLon } from '../utils/coordinates';\nimport type { GeocodeQuery, LatLon } from '../types';\n\nconst GEOCODE_URL = 'https://apiv3.geoportail.lu/geocode/search';\n\nexport interface GeocodeResultItem {\n latLon: LatLon;\n easting: number;\n northing: number;\n accuracy: number;\n street?: string;\n num?: string;\n zip?: string;\n locality?: string;\n}\n\nexport type GeocodeState =\n | { status: 'idle'; results: null; error: null }\n | { status: 'loading'; results: null; error: null }\n | { status: 'success'; results: GeocodeResultItem[]; error: null }\n | { status: 'error'; results: null; error: Error };\n\n/**\n * Hook for forward geocoding: search for a Luxembourg address and get coordinates.\n *\n * @example\n * const { state, search } = useGeocode();\n * search({ queryString: '1 rue du Fort Thüngen, Luxembourg' });\n * if (state.status === 'success') {\n * const { lat, lon } = state.results[0].latLon;\n * }\n */\nexport function useGeocode() {\n const [state, setState] = useState<GeocodeState>({\n status: 'idle',\n results: null,\n error: null,\n });\n\n const abortRef = useRef<AbortController | null>(null);\n\n useEffect(() => () => { abortRef.current?.abort(); }, []);\n\n const search = useCallback(async (query: GeocodeQuery) => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setState({ status: 'loading', results: null, error: null });\n\n try {\n const url = new URL(GEOCODE_URL);\n\n if (query.queryString) {\n url.searchParams.set('queryString', query.queryString);\n } else {\n if (query.num) url.searchParams.set('num', query.num);\n if (query.street) url.searchParams.set('street', query.street);\n if (query.zip) url.searchParams.set('zip', query.zip);\n if (query.locality) url.searchParams.set('locality', query.locality);\n }\n\n const res = await fetch(url.toString(), { signal: controller.signal });\n\n if (!res.ok) {\n throw new Error(`Geocode request failed: ${res.status} ${res.statusText}`);\n }\n\n const data = (await res.json()) as {\n results?: Array<{\n easting: number;\n northing: number;\n accuracy?: number;\n AddressDetails?: {\n street?: string;\n number?: string;\n zip?: string;\n locality?: string;\n };\n }>;\n };\n\n const results: GeocodeResultItem[] = (data.results ?? []).map((r) => ({\n latLon: lurefToLatLon(r.easting, r.northing),\n easting: r.easting,\n northing: r.northing,\n accuracy: r.accuracy ?? 0,\n street: r.AddressDetails?.street,\n num: r.AddressDetails?.number,\n zip: r.AddressDetails?.zip,\n locality: r.AddressDetails?.locality,\n }));\n\n setState({ status: 'success', results, error: null });\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === 'AbortError') return;\n setState({\n status: 'error',\n results: null,\n error: err instanceof Error ? err : new Error(String(err)),\n });\n }\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState({ status: 'idle', results: null, error: null });\n }, []);\n\n return { state, search, reset };\n}\n"],"names":["LUX_SCRIPT_URL","LUX_SCRIPT_ID","loadPromise","loadLuxApi","resolve","reject","pollForLux","script","attempts","check","useLuxApi","state","setState","useState","useEffect","cancelled","err","FE","FN","LON0","LAT0","SCALE","A","F","B","E2","DX","DY","DZ","degToRad","d","radToDeg","r","wgs84ToEd50","lat","lon","latR","lonR","sinLat","cosLat","sinLon","cosLon","aWgs","fWgs","bWgs","e2Wgs","da","df","N","dLat","dLon","ed50ToWgs84","M","tmForward","lon0R","lat0R","tanLat","T","C","dl","XX","meridianArc","M0","easting","northing","tmInverse","x","y","mu","e1","phi1","sinPhi1","cosPhi1","tanPhi1","N1","T1","C1","R1","D","e2","e4","e6","latLonToLuref","ed50","lurefToLatLon","LUXEMBOURG_CITY","DEFAULT_BG_LAYER","GeoportailMap","forwardRef","center","zoom","bgLayer","markerMode","markerPosition","onMarkerPlace","className","style","layers","ref","luxApi","mapContainerRef","useRef","mapRef","markerLayerRef","clickListenerRef","onMarkerPlaceRef","lux","mapInstance","map","clearMarker","initialPos","placeMarker","handler","args","evt","e","n","latLon","_a","useImperativeHandle","coords","z","jsx","jsxs","position","layerRef","ol","el","PIN_SVG","overlay","_map","REVERSE_GEOCODE_URL","useReverseGeocode","abortRef","lookup","useCallback","controller","url","res","first","_b","reset","GEOCODE_URL","useGeocode","search","query","results","_c","_d"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- export type { LuxCoordinate, LuxPosition, GeocodeResult, ReverseGeocodeResult, GeocodeResponse, ReverseGeocodeResponse, LuxMapOptions, LuxMapInstance, LuxGeocoderInstance, LuxNamespace, } from './lux.d.ts';
1
+ export type { LuxCoordinate, LuxPosition, GeocodeResult, ReverseGeocodeResult, GeocodeResponse, ReverseGeocodeResponse, LuxMapOptions, LuxMapInstance, LuxGeocoderInstance, LuxNamespace, } from './lux';
2
2
  /** WGS84 lat/lon coordinate */
3
3
  export interface LatLon {
4
4
  lat: number;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * TypeScript declarations for the Geoportail Luxembourg lux API (v3).
3
+ * Loaded dynamically via //apiv3.geoportail.lu/apiv3loader.js
4
+ */
5
+ export interface LuxCoordinate {
6
+ /** X coordinate in EPSG:2169 (Luxembourg TM) */
7
+ easting: number;
8
+ /** Y coordinate in EPSG:2169 (Luxembourg TM) */
9
+ northing: number;
10
+ }
11
+ export interface LuxPosition {
12
+ /** Longitude in WGS84 (EPSG:4326) */
13
+ lon: number;
14
+ /** Latitude in WGS84 (EPSG:4326) */
15
+ lat: number;
16
+ }
17
+ export interface GeocodeResult {
18
+ easting: number;
19
+ northing: number;
20
+ accuracy: number;
21
+ street?: string;
22
+ num?: string;
23
+ zip?: string;
24
+ locality?: string;
25
+ country?: string;
26
+ }
27
+ export interface ReverseGeocodeResult {
28
+ easting: number;
29
+ northing: number;
30
+ name: string;
31
+ distance: number;
32
+ }
33
+ export interface GeocodeResponse {
34
+ results: GeocodeResult[];
35
+ success: boolean;
36
+ }
37
+ export interface ReverseGeocodeResponse {
38
+ results: ReverseGeocodeResult[];
39
+ success: boolean;
40
+ }
41
+ export interface LuxMapOptions {
42
+ /** DOM element ID or HTMLElement to render the map into */
43
+ target: string | HTMLElement;
44
+ /** Background layer identifier (e.g. 'basemap_2015_global') */
45
+ bgLayer?: string;
46
+ /** Initial zoom level */
47
+ zoom?: number;
48
+ /** Initial position as [lon, lat] in EPSG:4326 or [x, y] in EPSG:2169 */
49
+ position?: [number, number];
50
+ /** Additional layer IDs to add on top of the background */
51
+ layers?: (number | string)[];
52
+ layerOpacities?: number[];
53
+ layerVisibilities?: boolean[];
54
+ }
55
+ export interface LuxMapInstance {
56
+ getTarget(): HTMLElement;
57
+ getLayers(): unknown;
58
+ addLayerToMap(layerId: number | string, opacity?: number, visible?: boolean): void;
59
+ on(event: string, handler: (...args: unknown[]) => void): void;
60
+ un(event: string, handler: (...args: unknown[]) => void): void;
61
+ getView(): {
62
+ getCenter(): [number, number];
63
+ getZoom(): number;
64
+ setCenter(center: [number, number]): void;
65
+ setZoom(zoom: number): void;
66
+ };
67
+ }
68
+ export interface LuxGeocoderInstance {
69
+ geocode(params: {
70
+ queryString?: string;
71
+ num?: string;
72
+ street?: string;
73
+ zip?: string;
74
+ locality?: string;
75
+ }, callback: (success: boolean, response: GeocodeResponse) => void): void;
76
+ reversegeocode(params: LuxCoordinate, callback: (success: boolean, response: ReverseGeocodeResponse) => void): void;
77
+ }
78
+ /** Global lux namespace injected by apiv3loader.js */
79
+ export interface LuxNamespace {
80
+ Map: new (options: LuxMapOptions) => LuxMapInstance;
81
+ Geocoder: new () => LuxGeocoderInstance;
82
+ }
83
+ declare global {
84
+ interface Window {
85
+ lux?: LuxNamespace;
86
+ }
87
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dacostafilipe/react-geoportail",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "React SDK for the Geoportail Luxembourg v3 API",
5
5
  "type": "module",
6
6
  "main": "./dist/react-geoportail.umd.cjs",
@@ -19,6 +19,7 @@
19
19
  "scripts": {
20
20
  "dev": "vite",
21
21
  "build": "tsc -p tsconfig.build.json && vite build",
22
+ "prepack": "npm run build",
22
23
  "preview": "vite preview",
23
24
  "type-check": "tsc --noEmit"
24
25
  },