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.mjs
CHANGED
|
@@ -78,18 +78,30 @@ var findXYAxisMedians = (vertices) => {
|
|
|
78
78
|
};
|
|
79
79
|
var getGlb = (data, customCenter) => __async(null, null, function* () {
|
|
80
80
|
const center = customCenter || findXYAxisMedians(data.surfaceDefinition.points);
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
const pts = data.surfaceDefinition.points;
|
|
82
|
+
const facesFlat = data.surfaceDefinition.faces;
|
|
83
|
+
const vertexBuffer = new Float32Array(pts.length * 3);
|
|
84
|
+
for (let i = 0; i < pts.length; i++) {
|
|
85
|
+
const [x, y, z] = pts[i];
|
|
86
|
+
vertexBuffer[i * 3] = x - center[0];
|
|
87
|
+
vertexBuffer[i * 3 + 1] = z;
|
|
88
|
+
vertexBuffer[i * 3 + 2] = -(y - center[1]);
|
|
89
|
+
}
|
|
90
|
+
const indexBuffer = new Uint32Array(facesFlat.length * 3);
|
|
91
|
+
for (let i = 0; i < facesFlat.length; i++) {
|
|
92
|
+
const [a, b, c] = facesFlat[i];
|
|
93
|
+
indexBuffer[i * 3] = a;
|
|
94
|
+
indexBuffer[i * 3 + 1] = b;
|
|
95
|
+
indexBuffer[i * 3 + 2] = c;
|
|
96
|
+
}
|
|
85
97
|
const doc = new Document();
|
|
86
98
|
const buffer = doc.createBuffer();
|
|
87
|
-
const position = doc.createAccessor().setType("VEC3").setArray(
|
|
88
|
-
const indices = doc.createAccessor().setType("SCALAR").setArray(
|
|
99
|
+
const position = doc.createAccessor().setType("VEC3").setArray(vertexBuffer).setBuffer(buffer);
|
|
100
|
+
const indices = doc.createAccessor().setType("SCALAR").setArray(indexBuffer).setBuffer(buffer);
|
|
89
101
|
const prim = doc.createPrimitive().setAttribute("POSITION", position).setIndices(indices);
|
|
90
102
|
const mesh = doc.createMesh().addPrimitive(prim);
|
|
91
103
|
const node = doc.createNode().setMesh(mesh);
|
|
92
|
-
|
|
104
|
+
doc.createScene().addChild(node);
|
|
93
105
|
const glb = yield new WebIO().writeBinary(doc);
|
|
94
106
|
return { glb, center };
|
|
95
107
|
});
|
|
@@ -133,14 +145,29 @@ var surfaceDefWorker = createEasyWebWorker(
|
|
|
133
145
|
})
|
|
134
146
|
);
|
|
135
147
|
} else if (task === "find-neighboring-faces") {
|
|
136
|
-
const { faces
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
148
|
+
const { faces } = message.payload;
|
|
149
|
+
const edgeMap = /* @__PURE__ */ new Map();
|
|
150
|
+
const edgeKey = (a, b) => a < b ? `${a},${b}` : `${b},${a}`;
|
|
151
|
+
for (let i = 0; i < faces.length; i++) {
|
|
152
|
+
const [v0, v1, v2] = faces[i];
|
|
153
|
+
for (const key of [edgeKey(v0, v1), edgeKey(v1, v2), edgeKey(v0, v2)]) {
|
|
154
|
+
const existing = edgeMap.get(key);
|
|
155
|
+
if (!existing) {
|
|
156
|
+
edgeMap.set(key, [i]);
|
|
157
|
+
} else if (existing.length === 1) {
|
|
158
|
+
existing.push(i);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const faceNeighbors = new Array(faces.length);
|
|
163
|
+
for (let i = 0; i < faces.length; i++) {
|
|
164
|
+
const [v0, v1, v2] = faces[i];
|
|
165
|
+
const resolve = (key) => {
|
|
166
|
+
const pair = edgeMap.get(key);
|
|
167
|
+
if (!pair || pair.length < 2) return -1;
|
|
168
|
+
return pair[0] === i ? pair[1] : pair[0];
|
|
169
|
+
};
|
|
170
|
+
faceNeighbors[i] = [resolve(edgeKey(v0, v1)), resolve(edgeKey(v1, v2)), resolve(edgeKey(v0, v2))];
|
|
144
171
|
}
|
|
145
172
|
message.resolve(faceNeighbors);
|
|
146
173
|
}
|
|
@@ -151,134 +178,99 @@ var surfaceDefWorker = createEasyWebWorker(
|
|
|
151
178
|
},
|
|
152
179
|
{ maxWorkers: 16 }
|
|
153
180
|
);
|
|
154
|
-
|
|
155
|
-
return
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (typeof ((_b = (_a = parsed.LandXML) == null ? void 0 : _a.Surfaces) == null ? void 0 : _b.Surface) === "undefined") {
|
|
163
|
-
throw new Error("LandXML doesn't contain any surfaces");
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
if (!Array.isArray(parsed.LandXML.Surfaces.Surface)) {
|
|
167
|
-
parsed.LandXML.Surfaces.Surface = [parsed.LandXML.Surfaces.Surface];
|
|
168
|
-
}
|
|
169
|
-
let sourceFile = parsed.LandXML.Project.attr.name || "Undefined source";
|
|
170
|
-
let timeStamp = parsed.LandXML.Application.attr.timeStamp || "";
|
|
171
|
-
let wktString = ((_e = (_d = (_c = parsed.LandXML) == null ? void 0 : _c.CoordinateSystem) == null ? void 0 : _d.attr) == null ? void 0 : _e.ogcWktCode) || void 0;
|
|
172
|
-
const surfaces = parsed.LandXML.Surfaces.Surface.map(
|
|
173
|
-
(surface) => __async(null, null, function* () {
|
|
174
|
-
return new Promise((resolve2, reject2) => __async(null, null, function* () {
|
|
175
|
-
const { name, desc } = surface.attr;
|
|
176
|
-
const Pnts = surface.Definition.Pnts.P;
|
|
177
|
-
const Faces = surface.Definition.Faces.F;
|
|
178
|
-
let ptsIdArray = [];
|
|
179
|
-
let faces = [];
|
|
180
|
-
let faceNeighbors = [];
|
|
181
|
-
if (Pnts.length > 1e4) {
|
|
182
|
-
const sliceIndexes = [...Array(20).keys()].map((i) => Math.round(Pnts.length / 20 * i));
|
|
183
|
-
ptsIdArray = (yield Promise.all(
|
|
184
|
-
sliceIndexes.map(
|
|
185
|
-
(v, i, a) => new Promise((resolve3, reject3) => __async(null, null, function* () {
|
|
186
|
-
const pts = yield surfaceDefWorker.send({
|
|
187
|
-
task: "parse-surface-points",
|
|
188
|
-
points: Pnts.slice(a[i], a[i + 1] || Pnts.length)
|
|
189
|
-
});
|
|
190
|
-
resolve3(pts);
|
|
191
|
-
}))
|
|
192
|
-
)
|
|
193
|
-
)).reduce((prev, curr) => [...prev, ...curr], []);
|
|
194
|
-
} else {
|
|
195
|
-
ptsIdArray = yield surfaceDefWorker.send({ task: "parse-surface-points", points: Pnts });
|
|
196
|
-
}
|
|
197
|
-
const points = ptsIdArray.map((v) => v[1]);
|
|
198
|
-
const pointsIdMap = ptsIdArray.map((v) => v[0]);
|
|
199
|
-
if (Faces.length > 1e4) {
|
|
200
|
-
const sliceIndexes = [...Array(20).keys()].map((i) => Math.round(Faces.length / 20 * i));
|
|
201
|
-
faces = (yield Promise.all(
|
|
202
|
-
sliceIndexes.map(
|
|
203
|
-
(v, i, a) => new Promise((resolve3, reject3) => __async(null, null, function* () {
|
|
204
|
-
const fcs = yield surfaceDefWorker.send({
|
|
205
|
-
task: "parse-surface-faces",
|
|
206
|
-
faces: Faces.slice(a[i], a[i + 1] || Faces.length),
|
|
207
|
-
idMap: pointsIdMap
|
|
208
|
-
});
|
|
209
|
-
resolve3(fcs);
|
|
210
|
-
}))
|
|
211
|
-
)
|
|
212
|
-
)).reduce((prev, curr) => [...prev, ...curr], []);
|
|
213
|
-
} else {
|
|
214
|
-
faces = yield surfaceDefWorker.send({
|
|
215
|
-
task: "parse-surface-faces",
|
|
216
|
-
faces: Faces,
|
|
217
|
-
idMap: pointsIdMap
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
if (Faces.length > 1e4) {
|
|
221
|
-
const sliceIndexes = [...Array(20).keys()].map((i) => Math.round((faces.length - 1) / 20 * i));
|
|
222
|
-
faceNeighbors = (yield Promise.all(
|
|
223
|
-
sliceIndexes.map(
|
|
224
|
-
(v, i, a) => new Promise((resolve3, reject3) => __async(null, null, function* () {
|
|
225
|
-
const fcs = yield surfaceDefWorker.send({
|
|
226
|
-
task: "find-neighboring-faces",
|
|
227
|
-
faces,
|
|
228
|
-
range: {
|
|
229
|
-
start: a[i],
|
|
230
|
-
end: a[i + 1] || faces.length
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
resolve3(fcs);
|
|
234
|
-
}))
|
|
235
|
-
)
|
|
236
|
-
)).reduce((prev, curr) => [...prev, ...curr], []);
|
|
237
|
-
} else {
|
|
238
|
-
faceNeighbors = yield surfaceDefWorker.send({
|
|
239
|
-
task: "find-neighboring-faces",
|
|
240
|
-
faces,
|
|
241
|
-
range: {
|
|
242
|
-
start: 0,
|
|
243
|
-
end: faces.length
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
resolve2({
|
|
248
|
-
sourceFile,
|
|
249
|
-
timeStamp: timeStamp || "",
|
|
250
|
-
name,
|
|
251
|
-
description: desc || "",
|
|
252
|
-
wktString,
|
|
253
|
-
surfaceDefinition: {
|
|
254
|
-
points,
|
|
255
|
-
faces,
|
|
256
|
-
faceNeighbors
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
}));
|
|
181
|
+
function parallelChunks(items, count, makePayload) {
|
|
182
|
+
return __async(this, null, function* () {
|
|
183
|
+
const sliceIndexes = [...Array(count).keys()].map((i) => Math.round(items.length / count * i));
|
|
184
|
+
const chunks = yield Promise.all(
|
|
185
|
+
sliceIndexes.map((start, i, arr) => {
|
|
186
|
+
var _a;
|
|
187
|
+
const end = (_a = arr[i + 1]) != null ? _a : items.length;
|
|
188
|
+
return surfaceDefWorker.send(makePayload(items.slice(start, end), start, end));
|
|
260
189
|
})
|
|
261
190
|
);
|
|
262
|
-
|
|
191
|
+
return chunks.reduce((acc, c) => acc.concat(c), []);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
var CHUNK_THRESHOLD = 1e4;
|
|
195
|
+
var CHUNK_COUNT = 20;
|
|
196
|
+
var parseXML = (xmlString) => __async(null, null, function* () {
|
|
197
|
+
var _a, _b, _c, _d, _e;
|
|
198
|
+
const parsed = convert.xml2js(xmlString, {
|
|
199
|
+
compact: true,
|
|
200
|
+
attributesKey: "attr",
|
|
201
|
+
textKey: "content"
|
|
202
|
+
});
|
|
203
|
+
if (typeof ((_b = (_a = parsed.LandXML) == null ? void 0 : _a.Surfaces) == null ? void 0 : _b.Surface) === "undefined") {
|
|
204
|
+
throw new Error("LandXML doesn't contain any surfaces");
|
|
205
|
+
}
|
|
206
|
+
if (!Array.isArray(parsed.LandXML.Surfaces.Surface)) {
|
|
207
|
+
parsed.LandXML.Surfaces.Surface = [parsed.LandXML.Surfaces.Surface];
|
|
208
|
+
}
|
|
209
|
+
const sourceFile = parsed.LandXML.Project.attr.name || "Undefined source";
|
|
210
|
+
const timeStamp = parsed.LandXML.Application.attr.timeStamp || "";
|
|
211
|
+
const wktString = ((_e = (_d = (_c = parsed.LandXML) == null ? void 0 : _c.CoordinateSystem) == null ? void 0 : _d.attr) == null ? void 0 : _e.ogcWktCode) || void 0;
|
|
212
|
+
const surfaces = parsed.LandXML.Surfaces.Surface.map((surface) => __async(null, null, function* () {
|
|
213
|
+
const { name, desc } = surface.attr;
|
|
214
|
+
const Pnts = surface.Definition.Pnts.P;
|
|
215
|
+
const Faces = surface.Definition.Faces.F;
|
|
216
|
+
const ptsIdArray = Pnts.length > CHUNK_THRESHOLD ? yield parallelChunks(Pnts, CHUNK_COUNT, (chunk) => ({
|
|
217
|
+
task: "parse-surface-points",
|
|
218
|
+
points: chunk
|
|
219
|
+
})) : yield surfaceDefWorker.send({
|
|
220
|
+
task: "parse-surface-points",
|
|
221
|
+
points: Pnts
|
|
222
|
+
});
|
|
223
|
+
const points = ptsIdArray.map((v) => v[1]);
|
|
224
|
+
const pointsIdMap = ptsIdArray.map((v) => v[0]);
|
|
225
|
+
const faces = Faces.length > CHUNK_THRESHOLD ? yield parallelChunks(Faces, CHUNK_COUNT, (chunk) => ({
|
|
226
|
+
task: "parse-surface-faces",
|
|
227
|
+
faces: chunk,
|
|
228
|
+
idMap: pointsIdMap
|
|
229
|
+
})) : yield surfaceDefWorker.send({
|
|
230
|
+
task: "parse-surface-faces",
|
|
231
|
+
faces: Faces,
|
|
232
|
+
idMap: pointsIdMap
|
|
233
|
+
});
|
|
234
|
+
const faceNeighbors = yield surfaceDefWorker.send({
|
|
235
|
+
task: "find-neighboring-faces",
|
|
236
|
+
faces
|
|
237
|
+
});
|
|
238
|
+
return {
|
|
239
|
+
sourceFile,
|
|
240
|
+
timeStamp,
|
|
241
|
+
name,
|
|
242
|
+
description: desc || "",
|
|
243
|
+
wktString,
|
|
244
|
+
surfaceDefinition: { points, faces, faceNeighbors }
|
|
245
|
+
};
|
|
263
246
|
}));
|
|
247
|
+
return Promise.all(surfaces);
|
|
264
248
|
});
|
|
265
249
|
var parse_xml_default = parseXML;
|
|
266
250
|
|
|
267
251
|
// src/public/to-glb.ts
|
|
268
252
|
var toGlb = (landXmlString, center = "auto", surfaceId = -1) => __async(null, null, function* () {
|
|
269
|
-
const requestedCenter = center == "origin" ? [0, 0] : center === "auto" ? void 0 : center;
|
|
270
253
|
let requestedParsedSurfaces = filter_by_surfaceId_default(yield parse_xml_default(landXmlString), surfaceId);
|
|
254
|
+
let resolvedCenter;
|
|
255
|
+
if (center === "origin") {
|
|
256
|
+
resolvedCenter = [0, 0];
|
|
257
|
+
} else if (center === "auto") {
|
|
258
|
+
const allPoints = requestedParsedSurfaces.flatMap((s) => s.surfaceDefinition.points);
|
|
259
|
+
resolvedCenter = findXYAxisMedians(allPoints);
|
|
260
|
+
} else {
|
|
261
|
+
resolvedCenter = center;
|
|
262
|
+
}
|
|
271
263
|
const glbs = yield Promise.all(
|
|
272
264
|
requestedParsedSurfaces.map(
|
|
273
265
|
(surface) => new Promise((resolve, reject) => __async(null, null, function* () {
|
|
274
266
|
try {
|
|
275
|
-
const { glb
|
|
267
|
+
const { glb } = yield get_glb_default(surface, resolvedCenter);
|
|
276
268
|
const _a = surface, { surfaceDefinition } = _a, rest = __objRest(_a, ["surfaceDefinition"]);
|
|
277
269
|
resolve(__spreadProps(__spreadValues({}, rest), {
|
|
278
270
|
glb,
|
|
279
|
-
center:
|
|
271
|
+
center: resolvedCenter,
|
|
280
272
|
download: () => {
|
|
281
|
-
download_glb_default(glb, surface.name.replace(/\.xml$/, `${JSON.stringify(
|
|
273
|
+
download_glb_default(glb, surface.name.replace(/\.xml$/, `${JSON.stringify(resolvedCenter)}.glb`));
|
|
282
274
|
}
|
|
283
275
|
}));
|
|
284
276
|
} catch (e) {
|
|
@@ -299,49 +291,45 @@ var contoursWorker = createEasyWebWorker2(
|
|
|
299
291
|
let vertsAtElevation = 0;
|
|
300
292
|
let line = [];
|
|
301
293
|
for (let i = 0; i < face.length; i++) {
|
|
302
|
-
|
|
303
|
-
|
|
294
|
+
const vertex1 = face[i];
|
|
295
|
+
const vertex2 = face[(i + 1) % face.length];
|
|
304
296
|
if (vertex1[2] === z) vertsAtElevation++;
|
|
305
297
|
if ((vertex1[2] <= z && vertex2[2] >= z || vertex1[2] >= z && vertex2[2] <= z) && !Number.isNaN((z - vertex1[2]) / (vertex2[2] - vertex1[2]))) {
|
|
306
|
-
|
|
298
|
+
const t = (z - vertex1[2]) / (vertex2[2] - vertex1[2]);
|
|
307
299
|
line.push([vertex1[0] + t * (vertex2[0] - vertex1[0]), vertex1[1] + t * (vertex2[1] - vertex1[1])]);
|
|
308
300
|
}
|
|
309
301
|
}
|
|
310
302
|
if (vertsAtElevation >= 2 && face.map((f) => f[2]).reduce((a, b) => a + b) > z * face.length) return void 0;
|
|
311
|
-
if (line.length === 2 && line[0][0] === line[1][0] && line[0][1] === line[1][1])
|
|
312
|
-
return void 0;
|
|
303
|
+
if (line.length === 2 && line[0][0] === line[1][0] && line[0][1] === line[1][1]) return void 0;
|
|
313
304
|
if (line.length > 2) {
|
|
314
305
|
line = [...new Set(line.map((v) => JSON.stringify(v)))].map((s) => JSON.parse(s));
|
|
315
306
|
}
|
|
316
307
|
return line.length > 0 ? line : void 0;
|
|
317
308
|
};
|
|
318
309
|
const linesToPolyLines3 = (lineSegments) => {
|
|
319
|
-
|
|
320
|
-
if (!Array.isArray(lineSegments) || (lineSegments == null ? void 0 : lineSegments.length) === 0) {
|
|
321
|
-
return [];
|
|
322
|
-
}
|
|
310
|
+
if (!Array.isArray(lineSegments) || lineSegments.length === 0) return [];
|
|
323
311
|
const segmentsMapIndexes = {};
|
|
324
312
|
const polylines = [];
|
|
325
|
-
const parsedSegmentIndexes =
|
|
313
|
+
const parsedSegmentIndexes = /* @__PURE__ */ new Set();
|
|
326
314
|
const lineSegmentStrings = lineSegments.map((v) => v.map((c) => c.join(",")));
|
|
327
|
-
lineSegmentStrings.forEach(([start, end], i) => {
|
|
328
|
-
segmentsMapIndexes[start] = segmentsMapIndexes[start] ? [...segmentsMapIndexes[start] || [], i] : [i];
|
|
329
|
-
segmentsMapIndexes[end] = segmentsMapIndexes[end] ? [...segmentsMapIndexes[end] || [], i] : [i];
|
|
330
|
-
});
|
|
331
315
|
for (let i = 0; i < lineSegmentStrings.length; i++) {
|
|
332
|
-
|
|
333
|
-
|
|
316
|
+
const [start, end] = lineSegmentStrings[i];
|
|
317
|
+
segmentsMapIndexes[start] = segmentsMapIndexes[start] ? [...segmentsMapIndexes[start], i] : [i];
|
|
318
|
+
segmentsMapIndexes[end] = segmentsMapIndexes[end] ? [...segmentsMapIndexes[end], i] : [i];
|
|
319
|
+
}
|
|
320
|
+
for (let i = 0; i < lineSegmentStrings.length; i++) {
|
|
321
|
+
if (parsedSegmentIndexes.has(i)) continue;
|
|
322
|
+
parsedSegmentIndexes.add(i);
|
|
334
323
|
let [start, end] = lineSegmentStrings[i];
|
|
335
|
-
|
|
324
|
+
const polyline = [start, end];
|
|
336
325
|
while (start && segmentsMapIndexes[start]) {
|
|
337
|
-
const nextLineIndex =
|
|
338
|
-
(
|
|
326
|
+
const nextLineIndex = segmentsMapIndexes[start].find(
|
|
327
|
+
(li) => !parsedSegmentIndexes.has(li)
|
|
339
328
|
);
|
|
340
|
-
if (nextLineIndex) {
|
|
341
|
-
parsedSegmentIndexes.
|
|
342
|
-
const
|
|
343
|
-
const
|
|
344
|
-
const newPoint = nextLineSegment[nextLineSegmentPointIndex];
|
|
329
|
+
if (nextLineIndex !== void 0) {
|
|
330
|
+
parsedSegmentIndexes.add(nextLineIndex);
|
|
331
|
+
const [a, b] = lineSegmentStrings[nextLineIndex];
|
|
332
|
+
const newPoint = a === start ? b : a;
|
|
345
333
|
polyline.unshift(newPoint);
|
|
346
334
|
start = newPoint;
|
|
347
335
|
} else {
|
|
@@ -349,14 +337,13 @@ var contoursWorker = createEasyWebWorker2(
|
|
|
349
337
|
}
|
|
350
338
|
}
|
|
351
339
|
while (end && segmentsMapIndexes[end]) {
|
|
352
|
-
const nextLineIndex =
|
|
353
|
-
(
|
|
340
|
+
const nextLineIndex = segmentsMapIndexes[end].find(
|
|
341
|
+
(li) => !parsedSegmentIndexes.has(li)
|
|
354
342
|
);
|
|
355
|
-
if (nextLineIndex) {
|
|
356
|
-
parsedSegmentIndexes.
|
|
357
|
-
const
|
|
358
|
-
const
|
|
359
|
-
const newPoint = nextLineSegment[nextLineSegmentPointIndex];
|
|
343
|
+
if (nextLineIndex !== void 0) {
|
|
344
|
+
parsedSegmentIndexes.add(nextLineIndex);
|
|
345
|
+
const [a, b] = lineSegmentStrings[nextLineIndex];
|
|
346
|
+
const newPoint = a === end ? b : a;
|
|
360
347
|
polyline.push(newPoint);
|
|
361
348
|
end = newPoint;
|
|
362
349
|
} else {
|
|
@@ -369,66 +356,51 @@ var contoursWorker = createEasyWebWorker2(
|
|
|
369
356
|
};
|
|
370
357
|
onMessage((message) => {
|
|
371
358
|
const { triangles, elevation } = message.payload;
|
|
372
|
-
const
|
|
359
|
+
const linesAtElevation = triangles.reduce((prev, curr) => {
|
|
373
360
|
const line = contourLineOnFace(curr, elevation);
|
|
374
361
|
if (line) prev.push(line);
|
|
375
362
|
return prev;
|
|
376
363
|
}, []);
|
|
377
|
-
message.resolve({
|
|
378
|
-
elevation,
|
|
379
|
-
polylines: linesToPolyLines3(linesAtElevationE)
|
|
380
|
-
});
|
|
364
|
+
message.resolve({ elevation, polylines: linesToPolyLines3(linesAtElevation) });
|
|
381
365
|
});
|
|
382
366
|
},
|
|
383
367
|
{ maxWorkers: 10 }
|
|
384
368
|
);
|
|
385
369
|
var linesToPolyLines = (lineSegments) => {
|
|
386
|
-
|
|
387
|
-
if (!Array.isArray(lineSegments) || (lineSegments == null ? void 0 : lineSegments.length) === 0) {
|
|
388
|
-
return [];
|
|
389
|
-
}
|
|
370
|
+
if (!Array.isArray(lineSegments) || lineSegments.length === 0) return [];
|
|
390
371
|
const segmentsMapIndexes = {};
|
|
391
372
|
const polylines = [];
|
|
392
|
-
const parsedSegmentIndexes =
|
|
373
|
+
const parsedSegmentIndexes = /* @__PURE__ */ new Set();
|
|
393
374
|
const lineSegmentStrings = lineSegments.map((v) => v.map((c) => c.join(",")));
|
|
394
|
-
lineSegmentStrings.forEach(([start, end], i) => {
|
|
395
|
-
segmentsMapIndexes[start] = segmentsMapIndexes[start] ? [...segmentsMapIndexes[start] || [], i] : [i];
|
|
396
|
-
segmentsMapIndexes[end] = segmentsMapIndexes[end] ? [...segmentsMapIndexes[end] || [], i] : [i];
|
|
397
|
-
});
|
|
398
375
|
for (let i = 0; i < lineSegmentStrings.length; i++) {
|
|
399
|
-
|
|
400
|
-
|
|
376
|
+
const [start, end] = lineSegmentStrings[i];
|
|
377
|
+
segmentsMapIndexes[start] = segmentsMapIndexes[start] ? [...segmentsMapIndexes[start], i] : [i];
|
|
378
|
+
segmentsMapIndexes[end] = segmentsMapIndexes[end] ? [...segmentsMapIndexes[end], i] : [i];
|
|
379
|
+
}
|
|
380
|
+
for (let i = 0; i < lineSegmentStrings.length; i++) {
|
|
381
|
+
if (parsedSegmentIndexes.has(i)) continue;
|
|
382
|
+
parsedSegmentIndexes.add(i);
|
|
401
383
|
let [start, end] = lineSegmentStrings[i];
|
|
402
|
-
|
|
384
|
+
const polyline = [start, end];
|
|
403
385
|
while (start && segmentsMapIndexes[start]) {
|
|
404
|
-
const nextLineIndex =
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
const nextLineSegment = lineSegmentStrings[nextLineIndex];
|
|
410
|
-
const nextLineSegmentPointIndex = nextLineSegment[0] === start ? 1 : 0;
|
|
411
|
-
const newPoint = nextLineSegment[nextLineSegmentPointIndex];
|
|
386
|
+
const nextLineIndex = segmentsMapIndexes[start].find((li) => !parsedSegmentIndexes.has(li));
|
|
387
|
+
if (nextLineIndex !== void 0) {
|
|
388
|
+
parsedSegmentIndexes.add(nextLineIndex);
|
|
389
|
+
const [a, b] = lineSegmentStrings[nextLineIndex];
|
|
390
|
+
const newPoint = a === start ? b : a;
|
|
412
391
|
polyline.unshift(newPoint);
|
|
413
392
|
start = newPoint;
|
|
414
|
-
} else
|
|
415
|
-
start = null;
|
|
416
|
-
}
|
|
393
|
+
} else start = null;
|
|
417
394
|
}
|
|
418
395
|
while (end && segmentsMapIndexes[end]) {
|
|
419
|
-
const nextLineIndex =
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const nextLineSegment = lineSegmentStrings[nextLineIndex];
|
|
425
|
-
const nextLineSegmentPointIndex = nextLineSegment[0] === end ? 1 : 0;
|
|
426
|
-
const newPoint = nextLineSegment[nextLineSegmentPointIndex];
|
|
396
|
+
const nextLineIndex = segmentsMapIndexes[end].find((li) => !parsedSegmentIndexes.has(li));
|
|
397
|
+
if (nextLineIndex !== void 0) {
|
|
398
|
+
parsedSegmentIndexes.add(nextLineIndex);
|
|
399
|
+
const [a, b] = lineSegmentStrings[nextLineIndex];
|
|
400
|
+
const newPoint = a === end ? b : a;
|
|
427
401
|
polyline.push(newPoint);
|
|
428
402
|
end = newPoint;
|
|
429
|
-
} else
|
|
430
|
-
end = null;
|
|
431
|
-
}
|
|
403
|
+
} else end = null;
|
|
432
404
|
}
|
|
433
405
|
polylines.push(polyline.map((coord) => coord.split(",").map((v) => parseFloat(v))));
|
|
434
406
|
}
|
|
@@ -442,10 +414,10 @@ var contourElevations = (minElevation, maxElevation, interval) => {
|
|
|
442
414
|
throw new Error(`No contour lines at interval: ${interval} between elevation ${minElevation} and ${maxElevation}`);
|
|
443
415
|
}
|
|
444
416
|
const elevations = [];
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
417
|
+
const firstStep = Math.ceil(minElevation / interval);
|
|
418
|
+
const lastStep = Math.ceil(maxElevation / interval) - 1;
|
|
419
|
+
for (let step = firstStep; step <= lastStep; step++) {
|
|
420
|
+
elevations.push(step * interval);
|
|
449
421
|
}
|
|
450
422
|
return elevations;
|
|
451
423
|
};
|
|
@@ -455,31 +427,55 @@ var constructGeojson = (elevationData) => {
|
|
|
455
427
|
return prev.concat(
|
|
456
428
|
polylines.map((polyline) => ({
|
|
457
429
|
type: "Feature",
|
|
458
|
-
geometry: {
|
|
459
|
-
|
|
460
|
-
coordinates: polyline
|
|
461
|
-
},
|
|
462
|
-
properties: {
|
|
463
|
-
z: elevation
|
|
464
|
-
}
|
|
430
|
+
geometry: { type: "LineString", coordinates: polyline },
|
|
431
|
+
properties: { z: elevation }
|
|
465
432
|
}))
|
|
466
433
|
);
|
|
467
434
|
}, []);
|
|
468
|
-
return {
|
|
469
|
-
type: "FeatureCollection",
|
|
470
|
-
features
|
|
471
|
-
};
|
|
435
|
+
return { type: "FeatureCollection", features };
|
|
472
436
|
};
|
|
473
|
-
var
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
const
|
|
478
|
-
([
|
|
479
|
-
[
|
|
480
|
-
|
|
437
|
+
var precomputeSurfaceData = (data) => {
|
|
438
|
+
const { points, faces } = data.surfaceDefinition;
|
|
439
|
+
let minElevation = Infinity;
|
|
440
|
+
let maxElevation = -Infinity;
|
|
441
|
+
for (const pt of points) {
|
|
442
|
+
if (pt[2] < minElevation) minElevation = pt[2];
|
|
443
|
+
if (pt[2] > maxElevation) maxElevation = pt[2];
|
|
444
|
+
}
|
|
445
|
+
const triangles = faces.map((face) => face.map((vert) => points[vert]));
|
|
446
|
+
return { triangles, minElevation, maxElevation };
|
|
447
|
+
};
|
|
448
|
+
var bucketTrianglesByElevation = (triangles, elevations, interval) => {
|
|
449
|
+
const trianglesByElevation = /* @__PURE__ */ new Map();
|
|
450
|
+
for (const e of elevations) trianglesByElevation.set(e, []);
|
|
451
|
+
for (const tri of triangles) {
|
|
452
|
+
const zMin = Math.min(tri[0][2], tri[1][2], tri[2][2]);
|
|
453
|
+
const zMax = Math.max(tri[0][2], tri[1][2], tri[2][2]);
|
|
454
|
+
const firstStep = Math.ceil(zMin / interval);
|
|
455
|
+
const lastStep = Math.floor(zMax / interval);
|
|
456
|
+
for (let step = firstStep; step <= lastStep; step++) {
|
|
457
|
+
const rounded = step * interval;
|
|
458
|
+
const bucket = trianglesByElevation.get(rounded);
|
|
459
|
+
if (bucket) bucket.push(tri);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return trianglesByElevation;
|
|
463
|
+
};
|
|
464
|
+
var getContours = (data, interval = 2, precomputed) => __async(null, null, function* () {
|
|
465
|
+
const { triangles, minElevation, maxElevation } = precomputed != null ? precomputed : precomputeSurfaceData(data);
|
|
481
466
|
const elevations = contourElevations(minElevation, maxElevation, interval);
|
|
482
|
-
const
|
|
467
|
+
const trianglesByElevation = bucketTrianglesByElevation(triangles, elevations, interval);
|
|
468
|
+
const elevationPolylines = yield Promise.all(
|
|
469
|
+
elevations.map(
|
|
470
|
+
(elevation) => {
|
|
471
|
+
var _a;
|
|
472
|
+
return contoursWorker.send({
|
|
473
|
+
triangles: (_a = trianglesByElevation.get(elevation)) != null ? _a : [],
|
|
474
|
+
elevation
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
)
|
|
478
|
+
);
|
|
483
479
|
return constructGeojson(elevationPolylines);
|
|
484
480
|
});
|
|
485
481
|
var get_contours_default = getContours;
|
|
@@ -562,8 +558,48 @@ var reprojectGeoJson = (geojson, sourceProjection, targetProjection = "WGS84", k
|
|
|
562
558
|
return geojson;
|
|
563
559
|
};
|
|
564
560
|
var reproject_geojson_default = reprojectGeoJson;
|
|
561
|
+
|
|
562
|
+
// src/public/to-glb-and-contours.ts
|
|
563
|
+
var toGlbAndContours = (landXmlString, contourInterval = 2, generateOutline = true, center = "auto", surfaceId = -1) => __async(null, null, function* () {
|
|
564
|
+
const requestedParsedSurfaces = filter_by_surfaceId_default(yield parse_xml_default(landXmlString), surfaceId);
|
|
565
|
+
let resolvedCenter;
|
|
566
|
+
if (center === "origin") {
|
|
567
|
+
resolvedCenter = [0, 0];
|
|
568
|
+
} else if (center === "auto") {
|
|
569
|
+
const allPoints = requestedParsedSurfaces.flatMap((s) => s.surfaceDefinition.points);
|
|
570
|
+
resolvedCenter = findXYAxisMedians(allPoints);
|
|
571
|
+
} else {
|
|
572
|
+
resolvedCenter = center;
|
|
573
|
+
}
|
|
574
|
+
const results = yield Promise.all(
|
|
575
|
+
requestedParsedSurfaces.map((surface) => __async(null, null, function* () {
|
|
576
|
+
const precomputed = precomputeSurfaceData(surface);
|
|
577
|
+
const [{ glb }, geojson] = yield Promise.all([
|
|
578
|
+
get_glb_default(surface, resolvedCenter),
|
|
579
|
+
get_contours_default(surface, contourInterval, precomputed)
|
|
580
|
+
]);
|
|
581
|
+
if (generateOutline) {
|
|
582
|
+
const outlineGeojson = get_outline_default(surface);
|
|
583
|
+
geojson.features = [...geojson.features, ...outlineGeojson.features];
|
|
584
|
+
}
|
|
585
|
+
const _a = surface, { surfaceDefinition } = _a, rest = __objRest(_a, ["surfaceDefinition"]);
|
|
586
|
+
return __spreadProps(__spreadValues({}, rest), {
|
|
587
|
+
glb,
|
|
588
|
+
center: resolvedCenter,
|
|
589
|
+
download: () => {
|
|
590
|
+
download_glb_default(glb, surface.name.replace(/\.xml$/, `${JSON.stringify(resolvedCenter)}.glb`));
|
|
591
|
+
},
|
|
592
|
+
geojson
|
|
593
|
+
});
|
|
594
|
+
}))
|
|
595
|
+
);
|
|
596
|
+
return results;
|
|
597
|
+
});
|
|
598
|
+
var to_glb_and_contours_default = toGlbAndContours;
|
|
565
599
|
export {
|
|
600
|
+
precomputeSurfaceData,
|
|
566
601
|
reproject_geojson_default as reprojectGeoJson,
|
|
567
602
|
to_geojson_contours_default as toGeojsonContours,
|
|
568
|
-
to_glb_default as toGlb
|
|
603
|
+
to_glb_default as toGlb,
|
|
604
|
+
to_glb_and_contours_default as toGlbAndContours
|
|
569
605
|
};
|