@georeferencing/react 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -27
- package/dist/Georeferencer.d.ts +18 -5
- package/dist/Georeferencer.js +123 -181
- package/dist/components/accuracy.d.ts +15 -0
- package/dist/components/accuracy.js +39 -0
- package/dist/components/exports.d.ts +32 -0
- package/dist/components/exports.js +152 -0
- package/dist/components/fields.d.ts +5 -2
- package/dist/components/fields.js +11 -6
- package/dist/controls.d.ts +40 -0
- package/dist/controls.js +28 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/panels/AlignmentPanel.js +5 -2
- package/dist/panels/FeaturePanel.js +12 -8
- package/dist/panels/GcpPanel.d.ts +19 -1
- package/dist/panels/GcpPanel.js +26 -10
- package/dist/panels/ImagePanel.d.ts +14 -1
- package/dist/panels/ImagePanel.js +56 -17
- package/dist/panels/PreviewControls.d.ts +3 -1
- package/dist/panels/PreviewControls.js +7 -6
- package/dist/styles.css +203 -49
- package/package.json +2 -2
|
@@ -11,7 +11,13 @@ export function ImagePanel({
|
|
|
11
11
|
/** Shared authoritative editor controller; the panel does not own its lifecycle. */
|
|
12
12
|
controller,
|
|
13
13
|
/** Optional translation function; defaults to returning English messages unchanged. */
|
|
14
|
-
t = identity,
|
|
14
|
+
t = identity,
|
|
15
|
+
/** Show previous/next view history and linked image/map navigation. */
|
|
16
|
+
navigation = true,
|
|
17
|
+
/** Show brightness, contrast and histogram display adjustment. */
|
|
18
|
+
displayAdjustment = true,
|
|
19
|
+
/** Extra actions inside the empty drop zone, such as a sample image button. */
|
|
20
|
+
emptyActions, }) {
|
|
15
21
|
const s = useGeoreferencer(controller), image = s.document.sourceImage, svg = useRef(null);
|
|
16
22
|
const view = s.imageView ?? [
|
|
17
23
|
0, 0, 100, 100,
|
|
@@ -113,31 +119,55 @@ t = identity, }) {
|
|
|
113
119
|
const p = new DOMPoint(e.clientX, e.clientY).matrixTransform(matrix.inverse());
|
|
114
120
|
return [p.x, p.y];
|
|
115
121
|
};
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
// Read the latest view from the controller so rapid wheel events never use a stale render.
|
|
123
|
+
const zoom = (factor, center) => {
|
|
124
|
+
const v = controller.getSnapshot().imageView ?? view;
|
|
125
|
+
const c = center ?? [v[0] + v[2] / 2, v[1] + v[3] / 2];
|
|
126
|
+
controller.setImageView([
|
|
127
|
+
c[0] + (v[0] - c[0]) * factor,
|
|
128
|
+
c[1] + (v[1] - c[1]) * factor,
|
|
129
|
+
Math.max(1, v[2] * factor),
|
|
130
|
+
Math.max(1, v[3] * factor),
|
|
131
|
+
]);
|
|
132
|
+
};
|
|
133
|
+
const zoomRef = useRef(zoom);
|
|
134
|
+
zoomRef.current = zoom;
|
|
135
|
+
const hasImage = Boolean(image);
|
|
136
|
+
// React registers wheel listeners as passive; a native one can stop the page scrolling.
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
const element = svg.current;
|
|
139
|
+
if (!hasImage || !element)
|
|
140
|
+
return;
|
|
141
|
+
const wheel = (e) => {
|
|
142
|
+
e.preventDefault();
|
|
143
|
+
const matrix = element.getScreenCTM();
|
|
144
|
+
const center = matrix
|
|
145
|
+
? new DOMPoint(e.clientX, e.clientY).matrixTransform(matrix.inverse())
|
|
146
|
+
: undefined;
|
|
147
|
+
zoomRef.current(e.deltaY > 0 ? 1.15 : 1 / 1.15, center ? [center.x, center.y] : undefined);
|
|
148
|
+
};
|
|
149
|
+
element.addEventListener("wheel", wheel, { passive: false });
|
|
150
|
+
return () => element.removeEventListener("wheel", wheel);
|
|
151
|
+
}, [hasImage]);
|
|
122
152
|
const down = (e) => {
|
|
123
153
|
if (!image)
|
|
124
154
|
return;
|
|
125
155
|
e.preventDefault();
|
|
126
156
|
e.currentTarget.focus({ preventScroll: true });
|
|
127
157
|
const p = point(e);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
controller.setPendingPoint(p);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
158
|
+
const inside = p[0] >= 0 && p[1] >= 0 && p[0] <= image.width && p[1] <= image.height;
|
|
159
|
+
// Dragging always pans; a click without movement places a point while matching.
|
|
133
160
|
drag.current = {
|
|
134
161
|
start: [e.clientX, e.clientY],
|
|
135
162
|
view,
|
|
136
163
|
pointerId: e.pointerId,
|
|
164
|
+
click: s.tool === "gcp" && e.button === 0 && !e.shiftKey && inside
|
|
165
|
+
? p
|
|
166
|
+
: undefined,
|
|
137
167
|
};
|
|
138
168
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
139
169
|
};
|
|
140
|
-
return (_jsxs("section", { className: "rg-image-panel", "aria-label": t("Source image"), onDragOver: (e) => e.preventDefault(), onDrop: (e) => {
|
|
170
|
+
return (_jsxs("section", { className: "rg-image-panel", "data-empty": image ? undefined : "", "aria-label": t("Source image"), onDragOver: (e) => e.preventDefault(), onDrop: (e) => {
|
|
141
171
|
e.preventDefault();
|
|
142
172
|
const file = e.dataTransfer.files[0];
|
|
143
173
|
if (file)
|
|
@@ -183,6 +213,10 @@ t = identity, }) {
|
|
|
183
213
|
setDragPoint({ id: d.id, point: d.point });
|
|
184
214
|
}
|
|
185
215
|
else {
|
|
216
|
+
if (!d.moved &&
|
|
217
|
+
Math.hypot(e.clientX - d.start[0], e.clientY - d.start[1]) < 4)
|
|
218
|
+
return;
|
|
219
|
+
d.moved = true;
|
|
186
220
|
const scale = Math.max(view[2] / e.currentTarget.clientWidth, view[3] / e.currentTarget.clientHeight);
|
|
187
221
|
setView([
|
|
188
222
|
d.view[0] - (e.clientX - d.start[0]) * scale,
|
|
@@ -196,14 +230,14 @@ t = identity, }) {
|
|
|
196
230
|
drag.current = null;
|
|
197
231
|
if (d?.id && d.point)
|
|
198
232
|
controller.updateGcp(d.id, { image: d.point });
|
|
233
|
+
else if (d?.click && !d.moved)
|
|
234
|
+
controller.setPendingPoint(d.click);
|
|
199
235
|
setDragPoint(null);
|
|
200
236
|
if (e.currentTarget.hasPointerCapture(e.pointerId))
|
|
201
237
|
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
202
238
|
}, onPointerCancel: () => {
|
|
203
239
|
drag.current = null;
|
|
204
240
|
setDragPoint(null);
|
|
205
|
-
}, onWheel: (e) => {
|
|
206
|
-
zoom(e.deltaY > 0 ? 1.15 : 1 / 1.15, point(e));
|
|
207
241
|
}, children: [_jsx("defs", { children: _jsx("filter", { id: filterId, colorInterpolationFilters: "sRGB", children: _jsxs("feComponentTransfer", { children: [_jsx("feFuncR", { type: "linear", slope: 255 / (stretch[0][1] - stretch[0][0]), intercept: -stretch[0][0] / (stretch[0][1] - stretch[0][0]) }), _jsx("feFuncG", { type: "linear", slope: 255 / (stretch[1][1] - stretch[1][0]), intercept: -stretch[1][0] / (stretch[1][1] - stretch[1][0]) }), _jsx("feFuncB", { type: "linear", slope: 255 / (stretch[2][1] - stretch[2][0]), intercept: -stretch[2][0] / (stretch[2][1] - stretch[2][0]) })] }) }) }), s.imageUrl && (_jsx("image", { href: s.imageUrl, width: image.width, height: image.height, style: {
|
|
208
242
|
filter: `url(#${filterId}) brightness(${brightness}) contrast(${contrast})`,
|
|
209
243
|
} })), s.document.gcps.map((gcp) => {
|
|
@@ -228,7 +262,7 @@ t = identity, }) {
|
|
|
228
262
|
-image.height * 0.06,
|
|
229
263
|
image.width * 1.08,
|
|
230
264
|
image.height * 1.12,
|
|
231
|
-
]), children: t("Fit image") }), _jsx("button", { type: "button", onClick: () => controller.navigateImageHistory(-1), children: t("Previous image view") }), _jsx("button", { type: "button", onClick: () => controller.navigateImageHistory(1), children: t("Next image view") }), _jsx("button", { type: "button", onClick: () => void controller.removeImage(), children: t("Remove image") })] }), _jsxs("label", { children: [t("Linked navigation"), _jsxs("select", { disabled: !s.fit, value: s.linkedNavigation, onChange: (e) => controller.setLinkedNavigation(e.target.value), children: [_jsx("option", { value: "off", children: t("Independent views") }), _jsx("option", { value: "image-to-map", children: t("Map follows image") }), _jsx("option", { value: "map-to-image", children: t("Image follows map") })] })] }), _jsxs("details", { children: [_jsx("summary", { children: t("Display adjustment & histogram") }), _jsxs("div", { className: "rg-toolbar", children: [_jsx("button", { type: "button", onClick: () => stretchHistogram(false), children: t("Full histogram stretch") }), _jsx("button", { type: "button", onClick: () => stretchHistogram(true), children: t("Local histogram stretch") }), _jsx("button", { type: "button", onClick: () => {
|
|
265
|
+
]), children: t("Fit image") }), navigation && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => controller.navigateImageHistory(-1), children: t("Previous image view") }), _jsx("button", { type: "button", onClick: () => controller.navigateImageHistory(1), children: t("Next image view") })] })), _jsx("button", { type: "button", onClick: () => void controller.removeImage(), children: t("Remove image") })] }), navigation && (_jsxs("label", { children: [t("Linked navigation"), _jsxs("select", { disabled: !s.fit, value: s.linkedNavigation, onChange: (e) => controller.setLinkedNavigation(e.target.value), children: [_jsx("option", { value: "off", children: t("Independent views") }), _jsx("option", { value: "image-to-map", children: t("Map follows image") }), _jsx("option", { value: "map-to-image", children: t("Image follows map") })] })] })), displayAdjustment && (_jsxs("details", { children: [_jsx("summary", { children: t("Display adjustment & histogram") }), _jsxs("div", { className: "rg-toolbar", children: [_jsx("button", { type: "button", onClick: () => stretchHistogram(false), children: t("Full histogram stretch") }), _jsx("button", { type: "button", onClick: () => stretchHistogram(true), children: t("Local histogram stretch") }), _jsx("button", { type: "button", onClick: () => {
|
|
232
266
|
setStretch([
|
|
233
267
|
[0, 255],
|
|
234
268
|
[0, 255],
|
|
@@ -236,5 +270,10 @@ t = identity, }) {
|
|
|
236
270
|
]);
|
|
237
271
|
setBrightness(1);
|
|
238
272
|
setContrast(1);
|
|
239
|
-
}, children: t("Reset display") })] }), _jsxs("label", { children: [t("Brightness"), _jsx("input", { type: "range", min: "0.25", max: "2", step: "0.05", value: brightness, onChange: (e) => setBrightness(Number(e.target.value)) })] }), _jsxs("label", { children: [t("Contrast"), _jsx("input", { type: "range", min: "0.25", max: "2", step: "0.05", value: contrast, onChange: (e) => setContrast(Number(e.target.value)) })] }), _jsx("svg", { viewBox: "0 0 320 50", "aria-label": t("RGB intensity histogram of preview"), className: "rg-histogram", children: histogram.map((v, i) => (_jsx("rect", { x: i * 10, y: 50 - (50 * v) / Math.max(1, ...histogram), width: "9", height: (50 * v) / Math.max(1, ...histogram), fill: "currentColor" }, i))) }), _jsx("small", { children: t("Display only. Original samples are preserved for export.") })] })] })) : (_jsxs("div", { className: "rg-empty", children: [_jsx("span", { className: "rg-cross", children: "\uFF0B" }), _jsx("p", { children: t("Drop a map, plan, or photograph here.") }), _jsx("small", { children: t("PNG, JPEG, WebP, ordinary TIFF · up to 25 MiB · stays on this device") })
|
|
273
|
+
}, children: t("Reset display") })] }), _jsxs("label", { children: [t("Brightness"), _jsx("input", { type: "range", min: "0.25", max: "2", step: "0.05", value: brightness, onChange: (e) => setBrightness(Number(e.target.value)) })] }), _jsxs("label", { children: [t("Contrast"), _jsx("input", { type: "range", min: "0.25", max: "2", step: "0.05", value: contrast, onChange: (e) => setContrast(Number(e.target.value)) })] }), _jsx("svg", { viewBox: "0 0 320 50", "aria-label": t("RGB intensity histogram of preview"), className: "rg-histogram", children: histogram.map((v, i) => (_jsx("rect", { x: i * 10, y: 50 - (50 * v) / Math.max(1, ...histogram), width: "9", height: (50 * v) / Math.max(1, ...histogram), fill: "currentColor" }, i))) }), _jsx("small", { children: t("Display only. Original samples are preserved for export.") })] }))] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "rg-image-meta", children: t("No image selected") }), _jsxs("div", { className: "rg-empty", children: [_jsx("span", { className: "rg-cross", children: "\uFF0B" }), _jsx("p", { children: t("Drop a map, plan, or photograph here.") }), _jsx("small", { children: t("PNG, JPEG, WebP, ordinary TIFF · up to 25 MiB · stays on this device") }), _jsxs("div", { className: "rg-toolbar rg-empty-actions", children: [_jsxs("label", { className: "rg-file rg-primary", children: [t("Choose image"), _jsx("input", { "aria-label": t("Choose image file"), type: "file", accept: "image/png,image/jpeg,image/webp,image/tiff,.tif,.tiff", onChange: (e) => {
|
|
274
|
+
const file = e.target.files?.[0];
|
|
275
|
+
if (file)
|
|
276
|
+
void controller.loadImage(file);
|
|
277
|
+
e.target.value = "";
|
|
278
|
+
} })] }), emptyActions] })] })] }))] }));
|
|
240
279
|
}
|
|
@@ -5,11 +5,13 @@ import type { Translate } from "../localization.js";
|
|
|
5
5
|
* enough complete, enabled pairs; the worker validates rank and domain. `onReview`
|
|
6
6
|
* fires only for a successful preview of the same document/alignment revision.
|
|
7
7
|
*/
|
|
8
|
-
export declare function PreviewControls({ controller, t, onReview, }: {
|
|
8
|
+
export declare function PreviewControls({ controller, t, onReview, modeSelector, }: {
|
|
9
9
|
/** Shared authoritative controller. */
|
|
10
10
|
controller: GeoreferencerController;
|
|
11
11
|
/** Translate default English labels. */
|
|
12
12
|
t?: Translate;
|
|
13
13
|
/** Optional transition from matching into review after a current preview is ready. */
|
|
14
14
|
onReview?: () => void;
|
|
15
|
+
/** Show the manual/automatic preview selector. Defaults to true. */
|
|
16
|
+
modeSelector?: boolean;
|
|
15
17
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -7,11 +7,12 @@ import { identity } from "../localization.js";
|
|
|
7
7
|
* enough complete, enabled pairs; the worker validates rank and domain. `onReview`
|
|
8
8
|
* fires only for a successful preview of the same document/alignment revision.
|
|
9
9
|
*/
|
|
10
|
-
export function PreviewControls({ controller, t = identity, onReview, }) {
|
|
10
|
+
export function PreviewControls({ controller, t = identity, onReview, modeSelector = true, }) {
|
|
11
11
|
const s = useGeoreferencer(controller), d = s.document;
|
|
12
12
|
const count = d.gcps.filter((p) => p.enabled).length;
|
|
13
|
-
const
|
|
14
|
-
|
|
13
|
+
const minimum = MODELS[d.model].minimum;
|
|
14
|
+
const missing = Math.max(0, minimum - count);
|
|
15
|
+
return (_jsxs("div", { className: "rg-preview-controls", children: [modeSelector && (_jsxs("label", { children: [t("Preview updates"), _jsxs("select", { value: s.previewMode, onChange: (e) => controller.setPreviewMode(e.target.value), children: [_jsx("option", { value: "manual", children: t("Manual") }), _jsx("option", { value: "automatic", children: t("Automatic") })] })] })), _jsxs("div", { children: [_jsx("button", { type: "button", className: "rg-primary", disabled: !s.imageUrl ||
|
|
15
16
|
!!s.pendingImagePoint ||
|
|
16
17
|
missing > 0 ||
|
|
17
18
|
s.fitting === "running" ||
|
|
@@ -30,7 +31,7 @@ export function PreviewControls({ controller, t = identity, onReview, }) {
|
|
|
30
31
|
? "Running alignment…"
|
|
31
32
|
: s.fit && s.previewMode === "automatic"
|
|
32
33
|
? "Review alignment"
|
|
33
|
-
: "Run alignment") }),
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
: "Run alignment") }), _jsx("small", { className: "rg-readiness", "aria-live": "polite", children: missing > 0
|
|
35
|
+
? `${count} ${t("of")} ${minimum} ${t("points matched")}`
|
|
36
|
+
: `${count} ${t(count === 1 ? "point matched" : "points matched")} · ${t("more points improve accuracy")}` })] })] }));
|
|
36
37
|
}
|
package/dist/styles.css
CHANGED
|
@@ -277,18 +277,19 @@
|
|
|
277
277
|
}
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
/* Guided
|
|
281
|
-
.rg-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
.rg-preview-controls label {
|
|
287
|
-
min-width: 115px;
|
|
280
|
+
/* Guided step-by-step layout; all selectors remain scoped to the editor. */
|
|
281
|
+
.rg-editor .rg-primary:hover:not(:disabled) {
|
|
282
|
+
background: #0e5a4e;
|
|
283
|
+
border-color: #0e5a4e;
|
|
284
|
+
color: white;
|
|
288
285
|
}
|
|
289
|
-
.rg-
|
|
290
|
-
|
|
291
|
-
|
|
286
|
+
.rg-visually-hidden {
|
|
287
|
+
position: absolute;
|
|
288
|
+
width: 1px;
|
|
289
|
+
height: 1px;
|
|
290
|
+
overflow: hidden;
|
|
291
|
+
clip-path: inset(50%);
|
|
292
|
+
white-space: nowrap;
|
|
292
293
|
}
|
|
293
294
|
.rg-guided {
|
|
294
295
|
padding: 18px;
|
|
@@ -296,45 +297,136 @@
|
|
|
296
297
|
.rg-steps {
|
|
297
298
|
display: flex;
|
|
298
299
|
flex-wrap: wrap;
|
|
299
|
-
gap:
|
|
300
|
+
gap: 8px 22px;
|
|
300
301
|
list-style: none;
|
|
301
302
|
margin: 0;
|
|
302
303
|
padding: 0 0 14px;
|
|
303
304
|
border-bottom: 1px solid var(--rg-border);
|
|
304
|
-
font-size:
|
|
305
|
+
font-size: 13px;
|
|
305
306
|
color: #5b6c65;
|
|
306
307
|
}
|
|
307
|
-
.rg-steps
|
|
308
|
+
.rg-steps li,
|
|
309
|
+
.rg-editor .rg-step-button {
|
|
310
|
+
display: inline-flex;
|
|
311
|
+
align-items: center;
|
|
312
|
+
gap: 8px;
|
|
313
|
+
}
|
|
314
|
+
.rg-editor .rg-step-button {
|
|
315
|
+
padding: 0;
|
|
316
|
+
border: 0;
|
|
317
|
+
background: none;
|
|
318
|
+
color: inherit;
|
|
319
|
+
font-size: inherit;
|
|
320
|
+
}
|
|
321
|
+
.rg-editor .rg-step-button:hover .rg-step-label {
|
|
322
|
+
text-decoration: underline;
|
|
323
|
+
}
|
|
324
|
+
.rg-step-index {
|
|
325
|
+
display: inline-grid;
|
|
326
|
+
place-items: center;
|
|
327
|
+
width: 24px;
|
|
328
|
+
height: 24px;
|
|
329
|
+
border-radius: 50%;
|
|
330
|
+
border: 1.5px solid #9dab9e;
|
|
331
|
+
font-size: 12px;
|
|
332
|
+
font-weight: 700;
|
|
333
|
+
font-variant-numeric: tabular-nums;
|
|
334
|
+
}
|
|
335
|
+
.rg-steps [data-state="current"] {
|
|
308
336
|
color: var(--rg-accent);
|
|
309
|
-
font-weight:
|
|
337
|
+
font-weight: 700;
|
|
338
|
+
}
|
|
339
|
+
.rg-steps [data-state="current"] .rg-step-index {
|
|
340
|
+
border-color: var(--rg-accent);
|
|
341
|
+
background: #e3efe9;
|
|
342
|
+
}
|
|
343
|
+
.rg-steps [data-state="done"] {
|
|
344
|
+
color: #20322f;
|
|
345
|
+
}
|
|
346
|
+
.rg-steps [data-state="done"] .rg-step-index {
|
|
347
|
+
border-color: var(--rg-accent);
|
|
348
|
+
background: var(--rg-accent);
|
|
349
|
+
color: white;
|
|
310
350
|
}
|
|
311
351
|
.rg-matching-bar {
|
|
312
|
-
display:
|
|
313
|
-
|
|
352
|
+
display: grid;
|
|
353
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
314
354
|
align-items: center;
|
|
315
|
-
gap:
|
|
316
|
-
padding:
|
|
355
|
+
gap: 8px 24px;
|
|
356
|
+
padding: 14px 0 10px;
|
|
357
|
+
}
|
|
358
|
+
.rg-guided .rg-matching-bar {
|
|
359
|
+
position: sticky;
|
|
360
|
+
top: 0;
|
|
361
|
+
z-index: 5;
|
|
362
|
+
background: var(--rg-paper);
|
|
363
|
+
border-bottom: 1px solid var(--rg-border);
|
|
317
364
|
}
|
|
318
365
|
.rg-instruction strong {
|
|
319
366
|
font-size: 16px;
|
|
320
367
|
}
|
|
321
368
|
.rg-instruction p {
|
|
322
|
-
max-width:
|
|
369
|
+
max-width: 640px;
|
|
370
|
+
margin: 4px 0 0;
|
|
323
371
|
font-size: 12px;
|
|
324
372
|
color: #5b6c65;
|
|
325
373
|
}
|
|
374
|
+
.rg-editor .rg-link {
|
|
375
|
+
padding: 0;
|
|
376
|
+
border: 0;
|
|
377
|
+
background: none;
|
|
378
|
+
color: var(--rg-accent);
|
|
379
|
+
font-size: inherit;
|
|
380
|
+
text-decoration: underline;
|
|
381
|
+
}
|
|
382
|
+
.rg-step-actions {
|
|
383
|
+
display: flex;
|
|
384
|
+
flex-wrap: wrap;
|
|
385
|
+
align-items: center;
|
|
386
|
+
justify-content: flex-end;
|
|
387
|
+
gap: 8px;
|
|
388
|
+
}
|
|
389
|
+
.rg-preview-controls {
|
|
390
|
+
display: flex;
|
|
391
|
+
align-items: center;
|
|
392
|
+
gap: 16px;
|
|
393
|
+
}
|
|
394
|
+
.rg-preview-controls label {
|
|
395
|
+
display: flex;
|
|
396
|
+
align-items: center;
|
|
397
|
+
gap: 8px;
|
|
398
|
+
margin: 0;
|
|
399
|
+
}
|
|
400
|
+
.rg-preview-controls > div {
|
|
401
|
+
text-align: right;
|
|
402
|
+
}
|
|
403
|
+
.rg-readiness {
|
|
404
|
+
display: block;
|
|
405
|
+
margin-top: 4px;
|
|
406
|
+
}
|
|
326
407
|
.rg-match-tools {
|
|
327
|
-
margin:
|
|
408
|
+
margin: 10px 0 12px;
|
|
328
409
|
}
|
|
329
|
-
.rg-
|
|
410
|
+
.rg-tools-end {
|
|
330
411
|
display: flex;
|
|
331
412
|
align-items: center;
|
|
332
|
-
gap:
|
|
413
|
+
gap: 14px;
|
|
333
414
|
margin-left: auto;
|
|
334
415
|
}
|
|
335
|
-
.rg-
|
|
416
|
+
.rg-editor .rg-inline-field {
|
|
417
|
+
display: flex;
|
|
418
|
+
align-items: center;
|
|
419
|
+
gap: 8px;
|
|
420
|
+
margin: 0;
|
|
421
|
+
white-space: nowrap;
|
|
422
|
+
}
|
|
423
|
+
.rg-editor .rg-inline-field select {
|
|
424
|
+
width: auto;
|
|
336
425
|
max-width: 225px;
|
|
337
426
|
}
|
|
427
|
+
.rg-guided .rg-dirty {
|
|
428
|
+
line-height: 1.6;
|
|
429
|
+
}
|
|
338
430
|
.rg-paired-views {
|
|
339
431
|
display: grid;
|
|
340
432
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
@@ -355,54 +447,124 @@
|
|
|
355
447
|
}
|
|
356
448
|
.rg-guided .rg-image-panel .rg-panel-heading {
|
|
357
449
|
min-height: 39px;
|
|
450
|
+
align-items: center;
|
|
358
451
|
}
|
|
359
452
|
.rg-guided .rg-image-panel .rg-panel-heading h2 {
|
|
360
453
|
margin: 0;
|
|
361
454
|
}
|
|
455
|
+
.rg-guided .rg-image-panel .rg-panel-heading .rg-file {
|
|
456
|
+
margin: 0;
|
|
457
|
+
}
|
|
458
|
+
.rg-guided .rg-image-panel[data-empty] .rg-panel-heading .rg-file {
|
|
459
|
+
display: none;
|
|
460
|
+
}
|
|
362
461
|
.rg-guided .rg-image-meta {
|
|
363
462
|
margin-top: 4px;
|
|
364
463
|
}
|
|
464
|
+
.rg-empty-actions {
|
|
465
|
+
justify-content: center;
|
|
466
|
+
margin-top: 16px;
|
|
467
|
+
}
|
|
365
468
|
.rg-view-tabs {
|
|
366
469
|
display: none;
|
|
470
|
+
border: 0;
|
|
471
|
+
margin: 0;
|
|
472
|
+
padding: 0;
|
|
367
473
|
}
|
|
368
474
|
.rg-review-panels {
|
|
369
475
|
display: grid;
|
|
370
476
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
371
|
-
gap:
|
|
372
|
-
padding: 18px 0;
|
|
477
|
+
gap: 18px;
|
|
478
|
+
padding: 18px 0 0;
|
|
373
479
|
}
|
|
374
|
-
.rg-review-panels section + section {
|
|
480
|
+
.rg-editor .rg-review-panels section + section {
|
|
375
481
|
margin-top: 0;
|
|
376
482
|
padding-top: 0;
|
|
377
483
|
border-top: none;
|
|
378
484
|
}
|
|
485
|
+
.rg-card {
|
|
486
|
+
min-width: 0;
|
|
487
|
+
padding: 16px;
|
|
488
|
+
border: 1px solid var(--rg-border);
|
|
489
|
+
border-radius: 6px;
|
|
490
|
+
background: white;
|
|
491
|
+
}
|
|
492
|
+
.rg-editor .rg-review-panels .rg-card {
|
|
493
|
+
padding: 16px;
|
|
494
|
+
}
|
|
495
|
+
.rg-editor .rg-card h3 {
|
|
496
|
+
margin: 16px 0 0;
|
|
497
|
+
font-size: 13px;
|
|
498
|
+
font-weight: 650;
|
|
499
|
+
}
|
|
500
|
+
.rg-accuracy {
|
|
501
|
+
margin-bottom: 12px;
|
|
502
|
+
padding: 10px 12px;
|
|
503
|
+
border-left: 4px solid #9dab9e;
|
|
504
|
+
background: #f4f6f1;
|
|
505
|
+
}
|
|
506
|
+
.rg-accuracy[data-quality="good"] {
|
|
507
|
+
border-left-color: #2f8a5b;
|
|
508
|
+
}
|
|
509
|
+
.rg-accuracy[data-quality="fair"] {
|
|
510
|
+
border-left-color: #c48e45;
|
|
511
|
+
}
|
|
512
|
+
.rg-accuracy[data-quality="poor"] {
|
|
513
|
+
border-left-color: #b4452d;
|
|
514
|
+
}
|
|
515
|
+
.rg-accuracy p {
|
|
516
|
+
margin: 4px 0;
|
|
517
|
+
font-size: 12px;
|
|
518
|
+
}
|
|
379
519
|
.rg-guided .rg-gcp-panel {
|
|
380
520
|
border-top: 1px solid var(--rg-border);
|
|
381
521
|
margin-top: 18px;
|
|
382
522
|
padding-top: 16px;
|
|
383
523
|
}
|
|
384
|
-
.rg-
|
|
385
|
-
|
|
386
|
-
|
|
524
|
+
.rg-editor .rg-review-panels .rg-gcp-panel {
|
|
525
|
+
margin-top: 0;
|
|
526
|
+
padding-top: 0;
|
|
527
|
+
border-top: none;
|
|
528
|
+
}
|
|
529
|
+
.rg-editor .rg-point-list {
|
|
530
|
+
width: auto;
|
|
531
|
+
min-width: 280px;
|
|
387
532
|
}
|
|
388
533
|
.rg-advanced {
|
|
389
534
|
margin-top: 16px;
|
|
390
535
|
}
|
|
536
|
+
.rg-export-progress {
|
|
537
|
+
display: flex;
|
|
538
|
+
align-items: center;
|
|
539
|
+
gap: 12px;
|
|
540
|
+
margin: 10px 0;
|
|
541
|
+
}
|
|
542
|
+
.rg-status:empty {
|
|
543
|
+
margin: 0;
|
|
544
|
+
padding: 0;
|
|
545
|
+
border: 0;
|
|
546
|
+
}
|
|
391
547
|
@media (max-width: 720px) {
|
|
392
548
|
.rg-guided {
|
|
393
549
|
padding: 12px;
|
|
394
550
|
}
|
|
551
|
+
.rg-steps li:not([data-state="current"]) .rg-step-label {
|
|
552
|
+
display: none;
|
|
553
|
+
}
|
|
395
554
|
.rg-matching-bar {
|
|
396
|
-
|
|
397
|
-
align-items: stretch;
|
|
398
|
-
gap: 4px;
|
|
555
|
+
grid-template-columns: minmax(0, 1fr);
|
|
399
556
|
}
|
|
557
|
+
.rg-step-actions,
|
|
400
558
|
.rg-preview-controls {
|
|
401
|
-
justify-content:
|
|
559
|
+
justify-content: flex-start;
|
|
402
560
|
}
|
|
403
|
-
.rg-
|
|
561
|
+
.rg-preview-controls > div {
|
|
562
|
+
text-align: left;
|
|
563
|
+
}
|
|
564
|
+
.rg-tools-end {
|
|
404
565
|
margin-left: 0;
|
|
405
566
|
width: 100%;
|
|
567
|
+
flex-wrap: wrap;
|
|
406
568
|
}
|
|
407
569
|
.rg-paired-views {
|
|
408
570
|
grid-template-columns: minmax(0, 1fr);
|
|
@@ -423,18 +585,10 @@
|
|
|
423
585
|
grid-template-columns: minmax(0, 1fr);
|
|
424
586
|
}
|
|
425
587
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
margin: 0;
|
|
433
|
-
padding: 0;
|
|
434
|
-
}
|
|
435
|
-
.rg-guided .rg-matching-bar {
|
|
436
|
-
position: sticky;
|
|
437
|
-
top: 0;
|
|
438
|
-
z-index: 5;
|
|
439
|
-
background: var(--rg-paper);
|
|
588
|
+
.rg-editor .rg-error {
|
|
589
|
+
padding: 8px 12px;
|
|
590
|
+
border-left: 3px solid #b4452d;
|
|
591
|
+
background: #fbefea;
|
|
592
|
+
color: #7a2614;
|
|
593
|
+
font-size: 12px;
|
|
440
594
|
}
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.2.0",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"./styles.css": "./dist/styles.css"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@georeferencing/core": "^0.
|
|
35
|
+
"@georeferencing/core": "^0.2.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"react": ">=19.3.0 <20",
|