landxml 0.6.6 → 0.8.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/CHANGELOG.md +20 -0
- package/README.md +176 -28
- package/dist/index.d.mts +74 -3
- package/dist/index.d.ts +74 -3
- package/dist/index.js +260 -222
- package/dist/index.mjs +257 -221
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -79,9 +79,11 @@ var __async = (__this, __arguments, generator) => {
|
|
|
79
79
|
// src/index.ts
|
|
80
80
|
var index_exports = {};
|
|
81
81
|
__export(index_exports, {
|
|
82
|
+
precomputeSurfaceData: () => precomputeSurfaceData,
|
|
82
83
|
reprojectGeoJson: () => reproject_geojson_default,
|
|
83
84
|
toGeojsonContours: () => to_geojson_contours_default,
|
|
84
|
-
toGlb: () => to_glb_default
|
|
85
|
+
toGlb: () => to_glb_default,
|
|
86
|
+
toGlbAndContours: () => to_glb_and_contours_default
|
|
85
87
|
});
|
|
86
88
|
module.exports = __toCommonJS(index_exports);
|
|
87
89
|
|
|
@@ -113,18 +115,30 @@ var findXYAxisMedians = (vertices) => {
|
|
|
113
115
|
};
|
|
114
116
|
var getGlb = (data, customCenter) => __async(null, null, function* () {
|
|
115
117
|
const center = customCenter || findXYAxisMedians(data.surfaceDefinition.points);
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
const pts = data.surfaceDefinition.points;
|
|
119
|
+
const facesFlat = data.surfaceDefinition.faces;
|
|
120
|
+
const vertexBuffer = new Float32Array(pts.length * 3);
|
|
121
|
+
for (let i = 0; i < pts.length; i++) {
|
|
122
|
+
const [x, y, z] = pts[i];
|
|
123
|
+
vertexBuffer[i * 3] = x - center[0];
|
|
124
|
+
vertexBuffer[i * 3 + 1] = z;
|
|
125
|
+
vertexBuffer[i * 3 + 2] = -(y - center[1]);
|
|
126
|
+
}
|
|
127
|
+
const indexBuffer = new Uint32Array(facesFlat.length * 3);
|
|
128
|
+
for (let i = 0; i < facesFlat.length; i++) {
|
|
129
|
+
const [a, b, c] = facesFlat[i];
|
|
130
|
+
indexBuffer[i * 3] = a;
|
|
131
|
+
indexBuffer[i * 3 + 1] = b;
|
|
132
|
+
indexBuffer[i * 3 + 2] = c;
|
|
133
|
+
}
|
|
120
134
|
const doc = new import_core.Document();
|
|
121
135
|
const buffer = doc.createBuffer();
|
|
122
|
-
const position = doc.createAccessor().setType("VEC3").setArray(
|
|
123
|
-
const indices = doc.createAccessor().setType("SCALAR").setArray(
|
|
136
|
+
const position = doc.createAccessor().setType("VEC3").setArray(vertexBuffer).setBuffer(buffer);
|
|
137
|
+
const indices = doc.createAccessor().setType("SCALAR").setArray(indexBuffer).setBuffer(buffer);
|
|
124
138
|
const prim = doc.createPrimitive().setAttribute("POSITION", position).setIndices(indices);
|
|
125
139
|
const mesh = doc.createMesh().addPrimitive(prim);
|
|
126
140
|
const node = doc.createNode().setMesh(mesh);
|
|
127
|
-
|
|
141
|
+
doc.createScene().addChild(node);
|
|
128
142
|
const glb = yield new import_core.WebIO().writeBinary(doc);
|
|
129
143
|
return { glb, center };
|
|
130
144
|
});
|
|
@@ -168,14 +182,29 @@ var surfaceDefWorker = (0, import_easy_web_worker.createEasyWebWorker)(
|
|
|
168
182
|
})
|
|
169
183
|
);
|
|
170
184
|
} else if (task === "find-neighboring-faces") {
|
|
171
|
-
const { faces
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
185
|
+
const { faces } = message.payload;
|
|
186
|
+
const edgeMap = /* @__PURE__ */ new Map();
|
|
187
|
+
const edgeKey = (a, b) => a < b ? `${a},${b}` : `${b},${a}`;
|
|
188
|
+
for (let i = 0; i < faces.length; i++) {
|
|
189
|
+
const [v0, v1, v2] = faces[i];
|
|
190
|
+
for (const key of [edgeKey(v0, v1), edgeKey(v1, v2), edgeKey(v0, v2)]) {
|
|
191
|
+
const existing = edgeMap.get(key);
|
|
192
|
+
if (!existing) {
|
|
193
|
+
edgeMap.set(key, [i]);
|
|
194
|
+
} else if (existing.length === 1) {
|
|
195
|
+
existing.push(i);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const faceNeighbors = new Array(faces.length);
|
|
200
|
+
for (let i = 0; i < faces.length; i++) {
|
|
201
|
+
const [v0, v1, v2] = faces[i];
|
|
202
|
+
const resolve = (key) => {
|
|
203
|
+
const pair = edgeMap.get(key);
|
|
204
|
+
if (!pair || pair.length < 2) return -1;
|
|
205
|
+
return pair[0] === i ? pair[1] : pair[0];
|
|
206
|
+
};
|
|
207
|
+
faceNeighbors[i] = [resolve(edgeKey(v0, v1)), resolve(edgeKey(v1, v2)), resolve(edgeKey(v0, v2))];
|
|
179
208
|
}
|
|
180
209
|
message.resolve(faceNeighbors);
|
|
181
210
|
}
|
|
@@ -186,134 +215,99 @@ var surfaceDefWorker = (0, import_easy_web_worker.createEasyWebWorker)(
|
|
|
186
215
|
},
|
|
187
216
|
{ maxWorkers: 16 }
|
|
188
217
|
);
|
|
189
|
-
|
|
190
|
-
return
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (typeof ((_b = (_a = parsed.LandXML) == null ? void 0 : _a.Surfaces) == null ? void 0 : _b.Surface) === "undefined") {
|
|
198
|
-
throw new Error("LandXML doesn't contain any surfaces");
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
if (!Array.isArray(parsed.LandXML.Surfaces.Surface)) {
|
|
202
|
-
parsed.LandXML.Surfaces.Surface = [parsed.LandXML.Surfaces.Surface];
|
|
203
|
-
}
|
|
204
|
-
let sourceFile = parsed.LandXML.Project.attr.name || "Undefined source";
|
|
205
|
-
let timeStamp = parsed.LandXML.Application.attr.timeStamp || "";
|
|
206
|
-
let wktString = ((_e = (_d = (_c = parsed.LandXML) == null ? void 0 : _c.CoordinateSystem) == null ? void 0 : _d.attr) == null ? void 0 : _e.ogcWktCode) || void 0;
|
|
207
|
-
const surfaces = parsed.LandXML.Surfaces.Surface.map(
|
|
208
|
-
(surface) => __async(null, null, function* () {
|
|
209
|
-
return new Promise((resolve2, reject2) => __async(null, null, function* () {
|
|
210
|
-
const { name, desc } = surface.attr;
|
|
211
|
-
const Pnts = surface.Definition.Pnts.P;
|
|
212
|
-
const Faces = surface.Definition.Faces.F;
|
|
213
|
-
let ptsIdArray = [];
|
|
214
|
-
let faces = [];
|
|
215
|
-
let faceNeighbors = [];
|
|
216
|
-
if (Pnts.length > 1e4) {
|
|
217
|
-
const sliceIndexes = [...Array(20).keys()].map((i) => Math.round(Pnts.length / 20 * i));
|
|
218
|
-
ptsIdArray = (yield Promise.all(
|
|
219
|
-
sliceIndexes.map(
|
|
220
|
-
(v, i, a) => new Promise((resolve3, reject3) => __async(null, null, function* () {
|
|
221
|
-
const pts = yield surfaceDefWorker.send({
|
|
222
|
-
task: "parse-surface-points",
|
|
223
|
-
points: Pnts.slice(a[i], a[i + 1] || Pnts.length)
|
|
224
|
-
});
|
|
225
|
-
resolve3(pts);
|
|
226
|
-
}))
|
|
227
|
-
)
|
|
228
|
-
)).reduce((prev, curr) => [...prev, ...curr], []);
|
|
229
|
-
} else {
|
|
230
|
-
ptsIdArray = yield surfaceDefWorker.send({ task: "parse-surface-points", points: Pnts });
|
|
231
|
-
}
|
|
232
|
-
const points = ptsIdArray.map((v) => v[1]);
|
|
233
|
-
const pointsIdMap = ptsIdArray.map((v) => v[0]);
|
|
234
|
-
if (Faces.length > 1e4) {
|
|
235
|
-
const sliceIndexes = [...Array(20).keys()].map((i) => Math.round(Faces.length / 20 * i));
|
|
236
|
-
faces = (yield Promise.all(
|
|
237
|
-
sliceIndexes.map(
|
|
238
|
-
(v, i, a) => new Promise((resolve3, reject3) => __async(null, null, function* () {
|
|
239
|
-
const fcs = yield surfaceDefWorker.send({
|
|
240
|
-
task: "parse-surface-faces",
|
|
241
|
-
faces: Faces.slice(a[i], a[i + 1] || Faces.length),
|
|
242
|
-
idMap: pointsIdMap
|
|
243
|
-
});
|
|
244
|
-
resolve3(fcs);
|
|
245
|
-
}))
|
|
246
|
-
)
|
|
247
|
-
)).reduce((prev, curr) => [...prev, ...curr], []);
|
|
248
|
-
} else {
|
|
249
|
-
faces = yield surfaceDefWorker.send({
|
|
250
|
-
task: "parse-surface-faces",
|
|
251
|
-
faces: Faces,
|
|
252
|
-
idMap: pointsIdMap
|
|
253
|
-
});
|
|
254
|
-
}
|
|
255
|
-
if (Faces.length > 1e4) {
|
|
256
|
-
const sliceIndexes = [...Array(20).keys()].map((i) => Math.round((faces.length - 1) / 20 * i));
|
|
257
|
-
faceNeighbors = (yield Promise.all(
|
|
258
|
-
sliceIndexes.map(
|
|
259
|
-
(v, i, a) => new Promise((resolve3, reject3) => __async(null, null, function* () {
|
|
260
|
-
const fcs = yield surfaceDefWorker.send({
|
|
261
|
-
task: "find-neighboring-faces",
|
|
262
|
-
faces,
|
|
263
|
-
range: {
|
|
264
|
-
start: a[i],
|
|
265
|
-
end: a[i + 1] || faces.length
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
resolve3(fcs);
|
|
269
|
-
}))
|
|
270
|
-
)
|
|
271
|
-
)).reduce((prev, curr) => [...prev, ...curr], []);
|
|
272
|
-
} else {
|
|
273
|
-
faceNeighbors = yield surfaceDefWorker.send({
|
|
274
|
-
task: "find-neighboring-faces",
|
|
275
|
-
faces,
|
|
276
|
-
range: {
|
|
277
|
-
start: 0,
|
|
278
|
-
end: faces.length
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
resolve2({
|
|
283
|
-
sourceFile,
|
|
284
|
-
timeStamp: timeStamp || "",
|
|
285
|
-
name,
|
|
286
|
-
description: desc || "",
|
|
287
|
-
wktString,
|
|
288
|
-
surfaceDefinition: {
|
|
289
|
-
points,
|
|
290
|
-
faces,
|
|
291
|
-
faceNeighbors
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
}));
|
|
218
|
+
function parallelChunks(items, count, makePayload) {
|
|
219
|
+
return __async(this, null, function* () {
|
|
220
|
+
const sliceIndexes = [...Array(count).keys()].map((i) => Math.round(items.length / count * i));
|
|
221
|
+
const chunks = yield Promise.all(
|
|
222
|
+
sliceIndexes.map((start, i, arr) => {
|
|
223
|
+
var _a;
|
|
224
|
+
const end = (_a = arr[i + 1]) != null ? _a : items.length;
|
|
225
|
+
return surfaceDefWorker.send(makePayload(items.slice(start, end), start, end));
|
|
295
226
|
})
|
|
296
227
|
);
|
|
297
|
-
|
|
228
|
+
return chunks.reduce((acc, c) => acc.concat(c), []);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
var CHUNK_THRESHOLD = 1e4;
|
|
232
|
+
var CHUNK_COUNT = 20;
|
|
233
|
+
var parseXML = (xmlString) => __async(null, null, function* () {
|
|
234
|
+
var _a, _b, _c, _d, _e;
|
|
235
|
+
const parsed = import_xml_js.default.xml2js(xmlString, {
|
|
236
|
+
compact: true,
|
|
237
|
+
attributesKey: "attr",
|
|
238
|
+
textKey: "content"
|
|
239
|
+
});
|
|
240
|
+
if (typeof ((_b = (_a = parsed.LandXML) == null ? void 0 : _a.Surfaces) == null ? void 0 : _b.Surface) === "undefined") {
|
|
241
|
+
throw new Error("LandXML doesn't contain any surfaces");
|
|
242
|
+
}
|
|
243
|
+
if (!Array.isArray(parsed.LandXML.Surfaces.Surface)) {
|
|
244
|
+
parsed.LandXML.Surfaces.Surface = [parsed.LandXML.Surfaces.Surface];
|
|
245
|
+
}
|
|
246
|
+
const sourceFile = parsed.LandXML.Project.attr.name || "Undefined source";
|
|
247
|
+
const timeStamp = parsed.LandXML.Application.attr.timeStamp || "";
|
|
248
|
+
const wktString = ((_e = (_d = (_c = parsed.LandXML) == null ? void 0 : _c.CoordinateSystem) == null ? void 0 : _d.attr) == null ? void 0 : _e.ogcWktCode) || void 0;
|
|
249
|
+
const surfaces = parsed.LandXML.Surfaces.Surface.map((surface) => __async(null, null, function* () {
|
|
250
|
+
const { name, desc } = surface.attr;
|
|
251
|
+
const Pnts = surface.Definition.Pnts.P;
|
|
252
|
+
const Faces = surface.Definition.Faces.F;
|
|
253
|
+
const ptsIdArray = Pnts.length > CHUNK_THRESHOLD ? yield parallelChunks(Pnts, CHUNK_COUNT, (chunk) => ({
|
|
254
|
+
task: "parse-surface-points",
|
|
255
|
+
points: chunk
|
|
256
|
+
})) : yield surfaceDefWorker.send({
|
|
257
|
+
task: "parse-surface-points",
|
|
258
|
+
points: Pnts
|
|
259
|
+
});
|
|
260
|
+
const points = ptsIdArray.map((v) => v[1]);
|
|
261
|
+
const pointsIdMap = ptsIdArray.map((v) => v[0]);
|
|
262
|
+
const faces = Faces.length > CHUNK_THRESHOLD ? yield parallelChunks(Faces, CHUNK_COUNT, (chunk) => ({
|
|
263
|
+
task: "parse-surface-faces",
|
|
264
|
+
faces: chunk,
|
|
265
|
+
idMap: pointsIdMap
|
|
266
|
+
})) : yield surfaceDefWorker.send({
|
|
267
|
+
task: "parse-surface-faces",
|
|
268
|
+
faces: Faces,
|
|
269
|
+
idMap: pointsIdMap
|
|
270
|
+
});
|
|
271
|
+
const faceNeighbors = yield surfaceDefWorker.send({
|
|
272
|
+
task: "find-neighboring-faces",
|
|
273
|
+
faces
|
|
274
|
+
});
|
|
275
|
+
return {
|
|
276
|
+
sourceFile,
|
|
277
|
+
timeStamp,
|
|
278
|
+
name,
|
|
279
|
+
description: desc || "",
|
|
280
|
+
wktString,
|
|
281
|
+
surfaceDefinition: { points, faces, faceNeighbors }
|
|
282
|
+
};
|
|
298
283
|
}));
|
|
284
|
+
return Promise.all(surfaces);
|
|
299
285
|
});
|
|
300
286
|
var parse_xml_default = parseXML;
|
|
301
287
|
|
|
302
288
|
// src/public/to-glb.ts
|
|
303
289
|
var toGlb = (landXmlString, center = "auto", surfaceId = -1) => __async(null, null, function* () {
|
|
304
|
-
const requestedCenter = center == "origin" ? [0, 0] : center === "auto" ? void 0 : center;
|
|
305
290
|
let requestedParsedSurfaces = filter_by_surfaceId_default(yield parse_xml_default(landXmlString), surfaceId);
|
|
291
|
+
let resolvedCenter;
|
|
292
|
+
if (center === "origin") {
|
|
293
|
+
resolvedCenter = [0, 0];
|
|
294
|
+
} else if (center === "auto") {
|
|
295
|
+
const allPoints = requestedParsedSurfaces.flatMap((s) => s.surfaceDefinition.points);
|
|
296
|
+
resolvedCenter = findXYAxisMedians(allPoints);
|
|
297
|
+
} else {
|
|
298
|
+
resolvedCenter = center;
|
|
299
|
+
}
|
|
306
300
|
const glbs = yield Promise.all(
|
|
307
301
|
requestedParsedSurfaces.map(
|
|
308
302
|
(surface) => new Promise((resolve, reject) => __async(null, null, function* () {
|
|
309
303
|
try {
|
|
310
|
-
const { glb
|
|
304
|
+
const { glb } = yield get_glb_default(surface, resolvedCenter);
|
|
311
305
|
const _a = surface, { surfaceDefinition } = _a, rest = __objRest(_a, ["surfaceDefinition"]);
|
|
312
306
|
resolve(__spreadProps(__spreadValues({}, rest), {
|
|
313
307
|
glb,
|
|
314
|
-
center:
|
|
308
|
+
center: resolvedCenter,
|
|
315
309
|
download: () => {
|
|
316
|
-
download_glb_default(glb, surface.name.replace(/\.xml$/, `${JSON.stringify(
|
|
310
|
+
download_glb_default(glb, surface.name.replace(/\.xml$/, `${JSON.stringify(resolvedCenter)}.glb`));
|
|
317
311
|
}
|
|
318
312
|
}));
|
|
319
313
|
} catch (e) {
|
|
@@ -334,49 +328,45 @@ var contoursWorker = (0, import_easy_web_worker2.createEasyWebWorker)(
|
|
|
334
328
|
let vertsAtElevation = 0;
|
|
335
329
|
let line = [];
|
|
336
330
|
for (let i = 0; i < face.length; i++) {
|
|
337
|
-
|
|
338
|
-
|
|
331
|
+
const vertex1 = face[i];
|
|
332
|
+
const vertex2 = face[(i + 1) % face.length];
|
|
339
333
|
if (vertex1[2] === z) vertsAtElevation++;
|
|
340
334
|
if ((vertex1[2] <= z && vertex2[2] >= z || vertex1[2] >= z && vertex2[2] <= z) && !Number.isNaN((z - vertex1[2]) / (vertex2[2] - vertex1[2]))) {
|
|
341
|
-
|
|
335
|
+
const t = (z - vertex1[2]) / (vertex2[2] - vertex1[2]);
|
|
342
336
|
line.push([vertex1[0] + t * (vertex2[0] - vertex1[0]), vertex1[1] + t * (vertex2[1] - vertex1[1])]);
|
|
343
337
|
}
|
|
344
338
|
}
|
|
345
339
|
if (vertsAtElevation >= 2 && face.map((f) => f[2]).reduce((a, b) => a + b) > z * face.length) return void 0;
|
|
346
|
-
if (line.length === 2 && line[0][0] === line[1][0] && line[0][1] === line[1][1])
|
|
347
|
-
return void 0;
|
|
340
|
+
if (line.length === 2 && line[0][0] === line[1][0] && line[0][1] === line[1][1]) return void 0;
|
|
348
341
|
if (line.length > 2) {
|
|
349
342
|
line = [...new Set(line.map((v) => JSON.stringify(v)))].map((s) => JSON.parse(s));
|
|
350
343
|
}
|
|
351
344
|
return line.length > 0 ? line : void 0;
|
|
352
345
|
};
|
|
353
346
|
const linesToPolyLines3 = (lineSegments) => {
|
|
354
|
-
|
|
355
|
-
if (!Array.isArray(lineSegments) || (lineSegments == null ? void 0 : lineSegments.length) === 0) {
|
|
356
|
-
return [];
|
|
357
|
-
}
|
|
347
|
+
if (!Array.isArray(lineSegments) || lineSegments.length === 0) return [];
|
|
358
348
|
const segmentsMapIndexes = {};
|
|
359
349
|
const polylines = [];
|
|
360
|
-
const parsedSegmentIndexes =
|
|
350
|
+
const parsedSegmentIndexes = /* @__PURE__ */ new Set();
|
|
361
351
|
const lineSegmentStrings = lineSegments.map((v) => v.map((c) => c.join(",")));
|
|
362
|
-
lineSegmentStrings.forEach(([start, end], i) => {
|
|
363
|
-
segmentsMapIndexes[start] = segmentsMapIndexes[start] ? [...segmentsMapIndexes[start] || [], i] : [i];
|
|
364
|
-
segmentsMapIndexes[end] = segmentsMapIndexes[end] ? [...segmentsMapIndexes[end] || [], i] : [i];
|
|
365
|
-
});
|
|
366
352
|
for (let i = 0; i < lineSegmentStrings.length; i++) {
|
|
367
|
-
|
|
368
|
-
|
|
353
|
+
const [start, end] = lineSegmentStrings[i];
|
|
354
|
+
segmentsMapIndexes[start] = segmentsMapIndexes[start] ? [...segmentsMapIndexes[start], i] : [i];
|
|
355
|
+
segmentsMapIndexes[end] = segmentsMapIndexes[end] ? [...segmentsMapIndexes[end], i] : [i];
|
|
356
|
+
}
|
|
357
|
+
for (let i = 0; i < lineSegmentStrings.length; i++) {
|
|
358
|
+
if (parsedSegmentIndexes.has(i)) continue;
|
|
359
|
+
parsedSegmentIndexes.add(i);
|
|
369
360
|
let [start, end] = lineSegmentStrings[i];
|
|
370
|
-
|
|
361
|
+
const polyline = [start, end];
|
|
371
362
|
while (start && segmentsMapIndexes[start]) {
|
|
372
|
-
const nextLineIndex =
|
|
373
|
-
(
|
|
363
|
+
const nextLineIndex = segmentsMapIndexes[start].find(
|
|
364
|
+
(li) => !parsedSegmentIndexes.has(li)
|
|
374
365
|
);
|
|
375
|
-
if (nextLineIndex) {
|
|
376
|
-
parsedSegmentIndexes.
|
|
377
|
-
const
|
|
378
|
-
const
|
|
379
|
-
const newPoint = nextLineSegment[nextLineSegmentPointIndex];
|
|
366
|
+
if (nextLineIndex !== void 0) {
|
|
367
|
+
parsedSegmentIndexes.add(nextLineIndex);
|
|
368
|
+
const [a, b] = lineSegmentStrings[nextLineIndex];
|
|
369
|
+
const newPoint = a === start ? b : a;
|
|
380
370
|
polyline.unshift(newPoint);
|
|
381
371
|
start = newPoint;
|
|
382
372
|
} else {
|
|
@@ -384,14 +374,13 @@ var contoursWorker = (0, import_easy_web_worker2.createEasyWebWorker)(
|
|
|
384
374
|
}
|
|
385
375
|
}
|
|
386
376
|
while (end && segmentsMapIndexes[end]) {
|
|
387
|
-
const nextLineIndex =
|
|
388
|
-
(
|
|
377
|
+
const nextLineIndex = segmentsMapIndexes[end].find(
|
|
378
|
+
(li) => !parsedSegmentIndexes.has(li)
|
|
389
379
|
);
|
|
390
|
-
if (nextLineIndex) {
|
|
391
|
-
parsedSegmentIndexes.
|
|
392
|
-
const
|
|
393
|
-
const
|
|
394
|
-
const newPoint = nextLineSegment[nextLineSegmentPointIndex];
|
|
380
|
+
if (nextLineIndex !== void 0) {
|
|
381
|
+
parsedSegmentIndexes.add(nextLineIndex);
|
|
382
|
+
const [a, b] = lineSegmentStrings[nextLineIndex];
|
|
383
|
+
const newPoint = a === end ? b : a;
|
|
395
384
|
polyline.push(newPoint);
|
|
396
385
|
end = newPoint;
|
|
397
386
|
} else {
|
|
@@ -404,66 +393,51 @@ var contoursWorker = (0, import_easy_web_worker2.createEasyWebWorker)(
|
|
|
404
393
|
};
|
|
405
394
|
onMessage((message) => {
|
|
406
395
|
const { triangles, elevation } = message.payload;
|
|
407
|
-
const
|
|
396
|
+
const linesAtElevation = triangles.reduce((prev, curr) => {
|
|
408
397
|
const line = contourLineOnFace(curr, elevation);
|
|
409
398
|
if (line) prev.push(line);
|
|
410
399
|
return prev;
|
|
411
400
|
}, []);
|
|
412
|
-
message.resolve({
|
|
413
|
-
elevation,
|
|
414
|
-
polylines: linesToPolyLines3(linesAtElevationE)
|
|
415
|
-
});
|
|
401
|
+
message.resolve({ elevation, polylines: linesToPolyLines3(linesAtElevation) });
|
|
416
402
|
});
|
|
417
403
|
},
|
|
418
404
|
{ maxWorkers: 10 }
|
|
419
405
|
);
|
|
420
406
|
var linesToPolyLines = (lineSegments) => {
|
|
421
|
-
|
|
422
|
-
if (!Array.isArray(lineSegments) || (lineSegments == null ? void 0 : lineSegments.length) === 0) {
|
|
423
|
-
return [];
|
|
424
|
-
}
|
|
407
|
+
if (!Array.isArray(lineSegments) || lineSegments.length === 0) return [];
|
|
425
408
|
const segmentsMapIndexes = {};
|
|
426
409
|
const polylines = [];
|
|
427
|
-
const parsedSegmentIndexes =
|
|
410
|
+
const parsedSegmentIndexes = /* @__PURE__ */ new Set();
|
|
428
411
|
const lineSegmentStrings = lineSegments.map((v) => v.map((c) => c.join(",")));
|
|
429
|
-
lineSegmentStrings.forEach(([start, end], i) => {
|
|
430
|
-
segmentsMapIndexes[start] = segmentsMapIndexes[start] ? [...segmentsMapIndexes[start] || [], i] : [i];
|
|
431
|
-
segmentsMapIndexes[end] = segmentsMapIndexes[end] ? [...segmentsMapIndexes[end] || [], i] : [i];
|
|
432
|
-
});
|
|
433
412
|
for (let i = 0; i < lineSegmentStrings.length; i++) {
|
|
434
|
-
|
|
435
|
-
|
|
413
|
+
const [start, end] = lineSegmentStrings[i];
|
|
414
|
+
segmentsMapIndexes[start] = segmentsMapIndexes[start] ? [...segmentsMapIndexes[start], i] : [i];
|
|
415
|
+
segmentsMapIndexes[end] = segmentsMapIndexes[end] ? [...segmentsMapIndexes[end], i] : [i];
|
|
416
|
+
}
|
|
417
|
+
for (let i = 0; i < lineSegmentStrings.length; i++) {
|
|
418
|
+
if (parsedSegmentIndexes.has(i)) continue;
|
|
419
|
+
parsedSegmentIndexes.add(i);
|
|
436
420
|
let [start, end] = lineSegmentStrings[i];
|
|
437
|
-
|
|
421
|
+
const polyline = [start, end];
|
|
438
422
|
while (start && segmentsMapIndexes[start]) {
|
|
439
|
-
const nextLineIndex =
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
const nextLineSegment = lineSegmentStrings[nextLineIndex];
|
|
445
|
-
const nextLineSegmentPointIndex = nextLineSegment[0] === start ? 1 : 0;
|
|
446
|
-
const newPoint = nextLineSegment[nextLineSegmentPointIndex];
|
|
423
|
+
const nextLineIndex = segmentsMapIndexes[start].find((li) => !parsedSegmentIndexes.has(li));
|
|
424
|
+
if (nextLineIndex !== void 0) {
|
|
425
|
+
parsedSegmentIndexes.add(nextLineIndex);
|
|
426
|
+
const [a, b] = lineSegmentStrings[nextLineIndex];
|
|
427
|
+
const newPoint = a === start ? b : a;
|
|
447
428
|
polyline.unshift(newPoint);
|
|
448
429
|
start = newPoint;
|
|
449
|
-
} else
|
|
450
|
-
start = null;
|
|
451
|
-
}
|
|
430
|
+
} else start = null;
|
|
452
431
|
}
|
|
453
432
|
while (end && segmentsMapIndexes[end]) {
|
|
454
|
-
const nextLineIndex =
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
const nextLineSegment = lineSegmentStrings[nextLineIndex];
|
|
460
|
-
const nextLineSegmentPointIndex = nextLineSegment[0] === end ? 1 : 0;
|
|
461
|
-
const newPoint = nextLineSegment[nextLineSegmentPointIndex];
|
|
433
|
+
const nextLineIndex = segmentsMapIndexes[end].find((li) => !parsedSegmentIndexes.has(li));
|
|
434
|
+
if (nextLineIndex !== void 0) {
|
|
435
|
+
parsedSegmentIndexes.add(nextLineIndex);
|
|
436
|
+
const [a, b] = lineSegmentStrings[nextLineIndex];
|
|
437
|
+
const newPoint = a === end ? b : a;
|
|
462
438
|
polyline.push(newPoint);
|
|
463
439
|
end = newPoint;
|
|
464
|
-
} else
|
|
465
|
-
end = null;
|
|
466
|
-
}
|
|
440
|
+
} else end = null;
|
|
467
441
|
}
|
|
468
442
|
polylines.push(polyline.map((coord) => coord.split(",").map((v) => parseFloat(v))));
|
|
469
443
|
}
|
|
@@ -477,10 +451,10 @@ var contourElevations = (minElevation, maxElevation, interval) => {
|
|
|
477
451
|
throw new Error(`No contour lines at interval: ${interval} between elevation ${minElevation} and ${maxElevation}`);
|
|
478
452
|
}
|
|
479
453
|
const elevations = [];
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
454
|
+
const firstStep = Math.ceil(minElevation / interval);
|
|
455
|
+
const lastStep = Math.ceil(maxElevation / interval) - 1;
|
|
456
|
+
for (let step = firstStep; step <= lastStep; step++) {
|
|
457
|
+
elevations.push(step * interval);
|
|
484
458
|
}
|
|
485
459
|
return elevations;
|
|
486
460
|
};
|
|
@@ -490,31 +464,55 @@ var constructGeojson = (elevationData) => {
|
|
|
490
464
|
return prev.concat(
|
|
491
465
|
polylines.map((polyline) => ({
|
|
492
466
|
type: "Feature",
|
|
493
|
-
geometry: {
|
|
494
|
-
|
|
495
|
-
coordinates: polyline
|
|
496
|
-
},
|
|
497
|
-
properties: {
|
|
498
|
-
z: elevation
|
|
499
|
-
}
|
|
467
|
+
geometry: { type: "LineString", coordinates: polyline },
|
|
468
|
+
properties: { z: elevation }
|
|
500
469
|
}))
|
|
501
470
|
);
|
|
502
471
|
}, []);
|
|
503
|
-
return {
|
|
504
|
-
type: "FeatureCollection",
|
|
505
|
-
features
|
|
506
|
-
};
|
|
472
|
+
return { type: "FeatureCollection", features };
|
|
507
473
|
};
|
|
508
|
-
var
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
const
|
|
513
|
-
([
|
|
514
|
-
[
|
|
515
|
-
|
|
474
|
+
var precomputeSurfaceData = (data) => {
|
|
475
|
+
const { points, faces } = data.surfaceDefinition;
|
|
476
|
+
let minElevation = Infinity;
|
|
477
|
+
let maxElevation = -Infinity;
|
|
478
|
+
for (const pt of points) {
|
|
479
|
+
if (pt[2] < minElevation) minElevation = pt[2];
|
|
480
|
+
if (pt[2] > maxElevation) maxElevation = pt[2];
|
|
481
|
+
}
|
|
482
|
+
const triangles = faces.map((face) => face.map((vert) => points[vert]));
|
|
483
|
+
return { triangles, minElevation, maxElevation };
|
|
484
|
+
};
|
|
485
|
+
var bucketTrianglesByElevation = (triangles, elevations, interval) => {
|
|
486
|
+
const trianglesByElevation = /* @__PURE__ */ new Map();
|
|
487
|
+
for (const e of elevations) trianglesByElevation.set(e, []);
|
|
488
|
+
for (const tri of triangles) {
|
|
489
|
+
const zMin = Math.min(tri[0][2], tri[1][2], tri[2][2]);
|
|
490
|
+
const zMax = Math.max(tri[0][2], tri[1][2], tri[2][2]);
|
|
491
|
+
const firstStep = Math.ceil(zMin / interval);
|
|
492
|
+
const lastStep = Math.floor(zMax / interval);
|
|
493
|
+
for (let step = firstStep; step <= lastStep; step++) {
|
|
494
|
+
const rounded = step * interval;
|
|
495
|
+
const bucket = trianglesByElevation.get(rounded);
|
|
496
|
+
if (bucket) bucket.push(tri);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return trianglesByElevation;
|
|
500
|
+
};
|
|
501
|
+
var getContours = (data, interval = 2, precomputed) => __async(null, null, function* () {
|
|
502
|
+
const { triangles, minElevation, maxElevation } = precomputed != null ? precomputed : precomputeSurfaceData(data);
|
|
516
503
|
const elevations = contourElevations(minElevation, maxElevation, interval);
|
|
517
|
-
const
|
|
504
|
+
const trianglesByElevation = bucketTrianglesByElevation(triangles, elevations, interval);
|
|
505
|
+
const elevationPolylines = yield Promise.all(
|
|
506
|
+
elevations.map(
|
|
507
|
+
(elevation) => {
|
|
508
|
+
var _a;
|
|
509
|
+
return contoursWorker.send({
|
|
510
|
+
triangles: (_a = trianglesByElevation.get(elevation)) != null ? _a : [],
|
|
511
|
+
elevation
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
)
|
|
515
|
+
);
|
|
518
516
|
return constructGeojson(elevationPolylines);
|
|
519
517
|
});
|
|
520
518
|
var get_contours_default = getContours;
|
|
@@ -597,9 +595,49 @@ var reprojectGeoJson = (geojson, sourceProjection, targetProjection = "WGS84", k
|
|
|
597
595
|
return geojson;
|
|
598
596
|
};
|
|
599
597
|
var reproject_geojson_default = reprojectGeoJson;
|
|
598
|
+
|
|
599
|
+
// src/public/to-glb-and-contours.ts
|
|
600
|
+
var toGlbAndContours = (landXmlString, contourInterval = 2, generateOutline = true, center = "auto", surfaceId = -1) => __async(null, null, function* () {
|
|
601
|
+
const requestedParsedSurfaces = filter_by_surfaceId_default(yield parse_xml_default(landXmlString), surfaceId);
|
|
602
|
+
let resolvedCenter;
|
|
603
|
+
if (center === "origin") {
|
|
604
|
+
resolvedCenter = [0, 0];
|
|
605
|
+
} else if (center === "auto") {
|
|
606
|
+
const allPoints = requestedParsedSurfaces.flatMap((s) => s.surfaceDefinition.points);
|
|
607
|
+
resolvedCenter = findXYAxisMedians(allPoints);
|
|
608
|
+
} else {
|
|
609
|
+
resolvedCenter = center;
|
|
610
|
+
}
|
|
611
|
+
const results = yield Promise.all(
|
|
612
|
+
requestedParsedSurfaces.map((surface) => __async(null, null, function* () {
|
|
613
|
+
const precomputed = precomputeSurfaceData(surface);
|
|
614
|
+
const [{ glb }, geojson] = yield Promise.all([
|
|
615
|
+
get_glb_default(surface, resolvedCenter),
|
|
616
|
+
get_contours_default(surface, contourInterval, precomputed)
|
|
617
|
+
]);
|
|
618
|
+
if (generateOutline) {
|
|
619
|
+
const outlineGeojson = get_outline_default(surface);
|
|
620
|
+
geojson.features = [...geojson.features, ...outlineGeojson.features];
|
|
621
|
+
}
|
|
622
|
+
const _a = surface, { surfaceDefinition } = _a, rest = __objRest(_a, ["surfaceDefinition"]);
|
|
623
|
+
return __spreadProps(__spreadValues({}, rest), {
|
|
624
|
+
glb,
|
|
625
|
+
center: resolvedCenter,
|
|
626
|
+
download: () => {
|
|
627
|
+
download_glb_default(glb, surface.name.replace(/\.xml$/, `${JSON.stringify(resolvedCenter)}.glb`));
|
|
628
|
+
},
|
|
629
|
+
geojson
|
|
630
|
+
});
|
|
631
|
+
}))
|
|
632
|
+
);
|
|
633
|
+
return results;
|
|
634
|
+
});
|
|
635
|
+
var to_glb_and_contours_default = toGlbAndContours;
|
|
600
636
|
// Annotate the CommonJS export names for ESM import in node:
|
|
601
637
|
0 && (module.exports = {
|
|
638
|
+
precomputeSurfaceData,
|
|
602
639
|
reprojectGeoJson,
|
|
603
640
|
toGeojsonContours,
|
|
604
|
-
toGlb
|
|
641
|
+
toGlb,
|
|
642
|
+
toGlbAndContours
|
|
605
643
|
});
|