Skip to content

Commit 7199e2b

Browse files
tariqksolimanac-61github-actions[bot]jtrobertsAmanda Chung
authored
Update Feature Branch (#823)
* Fix scale bar position in mobile mode (#811) * Fix scale bar position in mobile mode * chore: bump version to 4.1.12-20251119 [version bump] --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * Updates to Animation Tool (#812) * Added Layer Refresh Rate option for loading animation time steps * Added customizable animation export options (defaults to GIF only) * Use rectangular animation areas * Suppress build warning from ffmpeg * chore: bump version to 4.1.14-20251124 [version bump] --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * #813 STAC UI import/export (#814) * #813 STAC UI import/export 1 * #813 STAC import/export 2 * Fix Expanded TimeUI Hours timezone offset * Add new callback for newActiveFeature (#816) * Add new callback for newActiveFeature * chore: bump version to 4.1.15-20251201 [version bump] --------- Co-authored-by: Amanda Chung <Amanda.Chung@jpl.nasa.gov> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * Highlight buttons in extended timeline view as range indicator in mobile mode (#817) * Highlight buttons in extended timeline view as range indicator in mobile mode * chore: bump version to 4.1.16-20251203 [version bump] --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * #815 Update STAC Collection metadata (#818) * #815 Update STAC Collection metadata * #815 Update STAC Collection metadata 2 * chore: bump version to 4.1.15-20251203 [version bump] --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * #820 Fix DrawTool - Incrementer Value Multi-User Race Conditions (#821) * #820 Fix DrawTool - Incrementer Value Multi-User Race Conditions * chore: bump version to 4.1.18-20251205 [version bump] --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> * Fix issue with local versus UTC time in expanded timeline display (#819) * Fix issue with local versus UTC time in expanded timeline display * Consolidate function * Improve init-db logging --------- Co-authored-by: ac-61 <ac-61@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Joe T. Roberts <5315956+jtroberts@users.noreply.github.com> Co-authored-by: Amanda Chung <Amanda.Chung@jpl.nasa.gov>
1 parent 7325d5b commit 7199e2b

File tree

25 files changed

+2326
-223
lines changed

25 files changed

+2326
-223
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.vscode
22
.DS_Store
33
.env
4+
nul
45

56
/node_modules/
67
/ssl/*

API/Backend/Draw/routes/draw.js

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -532,18 +532,50 @@ const _templateConform = (req, from) => {
532532
let numVal = response.newValue.replace(start, "").replace(end, "");
533533
if (numVal != "#") {
534534
numVal = parseInt(numVal);
535+
536+
// Check if this value collides with existing values
537+
// Need to distinguish between editing an existing feature vs adding a new one
538+
let hasCollision = false;
539+
535540
if (existingProperties[t.field] === response.newValue) {
536-
// In case of a resave, make sure the id exists only once
541+
// Value didn't change from what was sent by frontend
542+
// Count occurrences excluding this feature's UUID
537543
let count = 0;
544+
for (let i = 0; i < layer.length; i++) {
545+
if (layer[i] == null) continue;
546+
let geojson = layer[i];
547+
if (geojson?.properties?.[t.field] != null) {
548+
let featuresVal = geojson?.properties?.[t.field];
549+
let extractedVal = parseInt(featuresVal.replace(start, "").replace(end, ""));
550+
// Count this occurrence only if it's a DIFFERENT feature (different UUID)
551+
if (extractedVal === numVal &&
552+
geojson?.properties?.uuid !== existingProperties.uuid) {
553+
count++;
554+
}
555+
}
556+
}
557+
// If ANY other feature has this value, it's a collision
558+
if (count > 0) {
559+
hasCollision = true;
560+
}
561+
} else {
562+
// Manual change to a different value - check if it already exists
563+
if (usedValues.indexOf(numVal) !== -1) {
564+
hasCollision = true;
565+
}
566+
}
567+
568+
if (hasCollision) {
569+
// Auto-assign next available value instead of error
570+
let bestVal = 0;
571+
usedValues.sort(function (a, b) {
572+
return a - b;
573+
});
574+
usedValues = [...new Set(usedValues)]; // makes it unique
538575
usedValues.forEach((v) => {
539-
if (numVal === v) count++;
576+
if (bestVal === v) bestVal++;
540577
});
541-
if (count > 1)
542-
response.error = `Incrementing field: '${t.field}' is not unique`;
543-
} else {
544-
// In case a manual change, make sure the id is unique
545-
if (usedValues.indexOf(numVal) !== -1)
546-
response.error = `Incrementing field: '${t.field}' is not unique`;
578+
response.newValue = start + bestVal + end;
547579
}
548580
}
549581
}
@@ -554,27 +586,6 @@ const _templateConform = (req, from) => {
554586
response.error = `Incrementing field: '${t.field}' must follow syntax: '${start}{#}${end}'`;
555587
}
556588

557-
// Check that incrementer is unique
558-
let numMatches = 0;
559-
for (let i = 0; i < layer.length; i++) {
560-
if (layer[i] == null) continue;
561-
let geojson = layer[i];
562-
if (geojson?.properties?.[t.field] != null) {
563-
let featuresVal = geojson?.properties?.[t.field];
564-
if (
565-
(value || "").indexOf("#") == -1 &&
566-
response.newValue === featuresVal &&
567-
geojson?.properties?.uuid != existingProperties.uuid
568-
) {
569-
numMatches++;
570-
}
571-
}
572-
}
573-
// If we're are editing and the value did not change, allow a single match
574-
if (numMatches > 0) {
575-
response.error = `Incrementing field: '${t.field}' is not unique`;
576-
}
577-
578589
return response;
579590
}
580591
});

API/Backend/Stac/routes/stac.js

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,75 @@ router.get("/collections", function (req, res, next) {
107107
});
108108
});
109109

110+
// Export collection with all items
111+
router.get("/collections/:collection/export", async function (req, res, next) {
112+
const { collection } = req.params;
113+
114+
const stacUrl = `http://${
115+
process.env.IS_DOCKER === "true" ? "stac-fastapi" : "localhost"
116+
}:${process.env.STAC_PORT || 8881}`;
117+
118+
try {
119+
// Fetch collection metadata
120+
const collectionResponse = await fetch(
121+
`${stacUrl}/collections/${collection}`,
122+
{
123+
method: "GET",
124+
headers: { "content-type": "application/json" },
125+
}
126+
);
127+
128+
if (!collectionResponse.ok) {
129+
throw new Error("Collection not found");
130+
}
131+
132+
const collectionData = await collectionResponse.json();
133+
134+
// Fetch all items with pagination
135+
let allItems = [];
136+
let nextUrl = `${stacUrl}/collections/${collection}/items?limit=10000`;
137+
138+
while (nextUrl) {
139+
const itemsResponse = await fetch(nextUrl, {
140+
method: "GET",
141+
headers: { "content-type": "application/json" },
142+
});
143+
144+
if (!itemsResponse.ok) {
145+
throw new Error("Failed to fetch items");
146+
}
147+
148+
const itemsData = await itemsResponse.json();
149+
allItems = allItems.concat(itemsData.features || []);
150+
151+
// Check for next page link
152+
const nextLink = itemsData.links?.find((link) => link.rel === "next");
153+
nextUrl = nextLink ? nextLink.href : null;
154+
}
155+
156+
// Combine into export format
157+
const exportData = {
158+
collection: collectionData,
159+
items: allItems,
160+
};
161+
162+
res.send({
163+
status: "success",
164+
body: exportData,
165+
});
166+
} catch (error) {
167+
logger(
168+
"error",
169+
"Failed to export STAC Collection",
170+
req.originalUrl,
171+
req,
172+
error
173+
);
174+
res.status(500).send({
175+
status: "failure",
176+
message: error.message || "Failed to export STAC Collection",
177+
});
178+
}
179+
});
180+
110181
module.exports = router;

configuration/webpack.config.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,5 +667,14 @@ module.exports = function (webpackEnv) {
667667
// Turn off performance processing because we utilize
668668
// our own hints via the FileSizeReporter
669669
performance: false,
670+
// Suppress warnings from node_modules dependencies
671+
ignoreWarnings: [
672+
// Suppress warning from @ffmpeg/ffmpeg worker.js. This is a known issue with the
673+
// library's dynamic imports for worker files, but it works correctly at runtime.
674+
{
675+
module: /node_modules\/@ffmpeg\/ffmpeg/,
676+
message: /Critical dependency: the request of a dependency is an expression/,
677+
},
678+
],
670679
};
671680
};

configure/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "configure",
3-
"version": "4.1.11-20251113",
3+
"version": "4.1.18-20251205",
44
"homepage": "./configure/build",
55
"private": true,
66
"dependencies": {

configure/src/core/ConfigureStore.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ export const ConfigureStore = createSlice({
3737
newStacCollection: false,
3838
stacCollectionItems: false,
3939
stacCollectionJson: false,
40+
editStacCollection: false,
4041
layersUsedByStacCollection: false,
4142
deleteStacCollection: false,
43+
importStacItems: false,
4244
uploadConfig: false,
4345
cloneConfig: false,
4446
deleteConfig: false,

configure/src/core/Maker.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -393,9 +393,9 @@ const getComponent = (
393393

394394
// Get the configurable disabled message or use a default
395395
const disabledMessage = com.disabledMessage || "This feature is disabled.";
396-
396+
397397
let inner;
398-
let disabled = false;
398+
let disabled = com.disabled || false;
399399
if (com.disableSwitch) {
400400
let switchVal = getIn(configuration, com.disableSwitch, null);
401401
if (switchVal == null) {
@@ -407,7 +407,7 @@ const getComponent = (
407407
switchVal = false;
408408
}
409409
}
410-
disabled = !switchVal;
410+
disabled = disabled || !switchVal;
411411
}
412412
const isRequired = isFieldRequired(com, layer, configuration);
413413
const fieldValue = value != null ? value : getIn(directConf, com.field, "");

configure/src/core/calls.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,18 @@ const c = {
114114
type: "DELETE",
115115
url: "stac/collections/:collection/items/:item",
116116
},
117+
stac_bulk_items: {
118+
type: "POST",
119+
url: "stac/collections/:collection/bulk_items",
120+
},
121+
stac_export_collection: {
122+
type: "GET",
123+
url: "api/stac/collections/:collection/export",
124+
},
125+
stac_update_collection: {
126+
type: "PUT",
127+
url: "stac/collections/:collection",
128+
},
117129
account_entries: {
118130
type: "GET",
119131
url: "api/accounts/entries",
@@ -197,8 +209,10 @@ function api(call, data, success, error) {
197209
delete data.forceParams;
198210
}
199211

200-
if (c[call].type === "POST") options.body = JSON.stringify(data);
201-
else if (c[call].type === "GET") options.data = JSON.stringify(data);
212+
if (c[call].type === "POST" || c[call].type === "PUT" || c[call].type === "PATCH")
213+
options.body = JSON.stringify(data);
214+
else if (c[call].type === "GET")
215+
options.data = JSON.stringify(data);
202216

203217
fetch(
204218
`${domain}${url}${

0 commit comments

Comments
 (0)