landxml 0.6.6 → 0.7.1

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/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 vertices = [...data.surfaceDefinition.points].map((p) => p.slice()).map(([x, y, z]) => {
117
- return [x - center[0], z, -(y - center[1])];
118
- }).reduce((prev, curr) => prev.concat(curr), []);
119
- const triangles = data.surfaceDefinition.faces.reduce((prev, curr) => prev.concat(curr), []);
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(new Float32Array(vertices)).setBuffer(buffer);
123
- const indices = doc.createAccessor().setType("SCALAR").setArray(new Uint32Array(triangles)).setBuffer(buffer);
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
- const scene = doc.createScene().addChild(node);
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, range: { start, end } } = message.payload;
172
- const faceNeighbors = [];
173
- for (let i = start; i < end; i++) {
174
- const sourceFace = faces[i];
175
- const neighborA = faces.findIndex((f, j) => f.findIndex((v) => v === sourceFace[0]) >= 0 && f.findIndex((v) => v === sourceFace[1]) >= 0 && j !== i);
176
- const neighborB = faces.findIndex((f, j) => f.findIndex((v) => v === sourceFace[1]) >= 0 && f.findIndex((v) => v === sourceFace[2]) >= 0 && j !== i);
177
- const neighborC = faces.findIndex((f, j) => f.findIndex((v) => v === sourceFace[0]) >= 0 && f.findIndex((v) => v === sourceFace[2]) >= 0 && j !== i);
178
- faceNeighbors.push([neighborA, neighborB, neighborC]);
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,116 +215,73 @@ var surfaceDefWorker = (0, import_easy_web_worker.createEasyWebWorker)(
186
215
  },
187
216
  { maxWorkers: 16 }
188
217
  );
189
- var parseXML = (xmlString) => __async(null, null, function* () {
190
- return new Promise((resolve, reject) => __async(null, null, function* () {
191
- var _a, _b, _c, _d, _e;
192
- const parsed = import_xml_js.default.xml2js(xmlString, {
193
- compact: true,
194
- attributesKey: "attr",
195
- textKey: "content"
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
- resolve(yield Promise.all(surfaces));
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
 
@@ -334,49 +320,45 @@ var contoursWorker = (0, import_easy_web_worker2.createEasyWebWorker)(
334
320
  let vertsAtElevation = 0;
335
321
  let line = [];
336
322
  for (let i = 0; i < face.length; i++) {
337
- let vertex1 = face[i];
338
- let vertex2 = face[(i + 1) % face.length];
323
+ const vertex1 = face[i];
324
+ const vertex2 = face[(i + 1) % face.length];
339
325
  if (vertex1[2] === z) vertsAtElevation++;
340
326
  if ((vertex1[2] <= z && vertex2[2] >= z || vertex1[2] >= z && vertex2[2] <= z) && !Number.isNaN((z - vertex1[2]) / (vertex2[2] - vertex1[2]))) {
341
- let t = (z - vertex1[2]) / (vertex2[2] - vertex1[2]);
327
+ const t = (z - vertex1[2]) / (vertex2[2] - vertex1[2]);
342
328
  line.push([vertex1[0] + t * (vertex2[0] - vertex1[0]), vertex1[1] + t * (vertex2[1] - vertex1[1])]);
343
329
  }
344
330
  }
345
331
  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;
332
+ if (line.length === 2 && line[0][0] === line[1][0] && line[0][1] === line[1][1]) return void 0;
348
333
  if (line.length > 2) {
349
334
  line = [...new Set(line.map((v) => JSON.stringify(v)))].map((s) => JSON.parse(s));
350
335
  }
351
336
  return line.length > 0 ? line : void 0;
352
337
  };
353
338
  const linesToPolyLines3 = (lineSegments) => {
354
- var _a, _b;
355
- if (!Array.isArray(lineSegments) || (lineSegments == null ? void 0 : lineSegments.length) === 0) {
356
- return [];
357
- }
339
+ if (!Array.isArray(lineSegments) || lineSegments.length === 0) return [];
358
340
  const segmentsMapIndexes = {};
359
341
  const polylines = [];
360
- const parsedSegmentIndexes = [];
342
+ const parsedSegmentIndexes = /* @__PURE__ */ new Set();
361
343
  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
344
  for (let i = 0; i < lineSegmentStrings.length; i++) {
367
- if (parsedSegmentIndexes.includes(i)) continue;
368
- parsedSegmentIndexes.push(i);
345
+ const [start, end] = lineSegmentStrings[i];
346
+ segmentsMapIndexes[start] = segmentsMapIndexes[start] ? [...segmentsMapIndexes[start], i] : [i];
347
+ segmentsMapIndexes[end] = segmentsMapIndexes[end] ? [...segmentsMapIndexes[end], i] : [i];
348
+ }
349
+ for (let i = 0; i < lineSegmentStrings.length; i++) {
350
+ if (parsedSegmentIndexes.has(i)) continue;
351
+ parsedSegmentIndexes.add(i);
369
352
  let [start, end] = lineSegmentStrings[i];
370
- let polyline = [start, end];
353
+ const polyline = [start, end];
371
354
  while (start && segmentsMapIndexes[start]) {
372
- const nextLineIndex = (_a = segmentsMapIndexes[start]) == null ? void 0 : _a.find(
373
- (lineIndex) => !parsedSegmentIndexes.includes(lineIndex)
355
+ const nextLineIndex = segmentsMapIndexes[start].find(
356
+ (li) => !parsedSegmentIndexes.has(li)
374
357
  );
375
- if (nextLineIndex) {
376
- parsedSegmentIndexes.push(nextLineIndex);
377
- const nextLineSegment = lineSegmentStrings[nextLineIndex];
378
- const nextLineSegmentPointIndex = nextLineSegment[0] === start ? 1 : 0;
379
- const newPoint = nextLineSegment[nextLineSegmentPointIndex];
358
+ if (nextLineIndex !== void 0) {
359
+ parsedSegmentIndexes.add(nextLineIndex);
360
+ const [a, b] = lineSegmentStrings[nextLineIndex];
361
+ const newPoint = a === start ? b : a;
380
362
  polyline.unshift(newPoint);
381
363
  start = newPoint;
382
364
  } else {
@@ -384,14 +366,13 @@ var contoursWorker = (0, import_easy_web_worker2.createEasyWebWorker)(
384
366
  }
385
367
  }
386
368
  while (end && segmentsMapIndexes[end]) {
387
- const nextLineIndex = (_b = segmentsMapIndexes[end]) == null ? void 0 : _b.find(
388
- (lineIndex) => !parsedSegmentIndexes.includes(lineIndex)
369
+ const nextLineIndex = segmentsMapIndexes[end].find(
370
+ (li) => !parsedSegmentIndexes.has(li)
389
371
  );
390
- if (nextLineIndex) {
391
- parsedSegmentIndexes.push(nextLineIndex);
392
- const nextLineSegment = lineSegmentStrings[nextLineIndex];
393
- const nextLineSegmentPointIndex = nextLineSegment[0] === end ? 1 : 0;
394
- const newPoint = nextLineSegment[nextLineSegmentPointIndex];
372
+ if (nextLineIndex !== void 0) {
373
+ parsedSegmentIndexes.add(nextLineIndex);
374
+ const [a, b] = lineSegmentStrings[nextLineIndex];
375
+ const newPoint = a === end ? b : a;
395
376
  polyline.push(newPoint);
396
377
  end = newPoint;
397
378
  } else {
@@ -404,66 +385,51 @@ var contoursWorker = (0, import_easy_web_worker2.createEasyWebWorker)(
404
385
  };
405
386
  onMessage((message) => {
406
387
  const { triangles, elevation } = message.payload;
407
- const linesAtElevationE = triangles.reduce((prev, curr) => {
388
+ const linesAtElevation = triangles.reduce((prev, curr) => {
408
389
  const line = contourLineOnFace(curr, elevation);
409
390
  if (line) prev.push(line);
410
391
  return prev;
411
392
  }, []);
412
- message.resolve({
413
- elevation,
414
- polylines: linesToPolyLines3(linesAtElevationE)
415
- });
393
+ message.resolve({ elevation, polylines: linesToPolyLines3(linesAtElevation) });
416
394
  });
417
395
  },
418
396
  { maxWorkers: 10 }
419
397
  );
420
398
  var linesToPolyLines = (lineSegments) => {
421
- var _a, _b;
422
- if (!Array.isArray(lineSegments) || (lineSegments == null ? void 0 : lineSegments.length) === 0) {
423
- return [];
424
- }
399
+ if (!Array.isArray(lineSegments) || lineSegments.length === 0) return [];
425
400
  const segmentsMapIndexes = {};
426
401
  const polylines = [];
427
- const parsedSegmentIndexes = [];
402
+ const parsedSegmentIndexes = /* @__PURE__ */ new Set();
428
403
  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
404
  for (let i = 0; i < lineSegmentStrings.length; i++) {
434
- if (parsedSegmentIndexes.includes(i)) continue;
435
- parsedSegmentIndexes.push(i);
405
+ const [start, end] = lineSegmentStrings[i];
406
+ segmentsMapIndexes[start] = segmentsMapIndexes[start] ? [...segmentsMapIndexes[start], i] : [i];
407
+ segmentsMapIndexes[end] = segmentsMapIndexes[end] ? [...segmentsMapIndexes[end], i] : [i];
408
+ }
409
+ for (let i = 0; i < lineSegmentStrings.length; i++) {
410
+ if (parsedSegmentIndexes.has(i)) continue;
411
+ parsedSegmentIndexes.add(i);
436
412
  let [start, end] = lineSegmentStrings[i];
437
- let polyline = [start, end];
413
+ const polyline = [start, end];
438
414
  while (start && segmentsMapIndexes[start]) {
439
- const nextLineIndex = (_a = segmentsMapIndexes[start]) == null ? void 0 : _a.find(
440
- (lineIndex) => !parsedSegmentIndexes.includes(lineIndex)
441
- );
442
- if (nextLineIndex) {
443
- parsedSegmentIndexes.push(nextLineIndex);
444
- const nextLineSegment = lineSegmentStrings[nextLineIndex];
445
- const nextLineSegmentPointIndex = nextLineSegment[0] === start ? 1 : 0;
446
- const newPoint = nextLineSegment[nextLineSegmentPointIndex];
415
+ const nextLineIndex = segmentsMapIndexes[start].find((li) => !parsedSegmentIndexes.has(li));
416
+ if (nextLineIndex !== void 0) {
417
+ parsedSegmentIndexes.add(nextLineIndex);
418
+ const [a, b] = lineSegmentStrings[nextLineIndex];
419
+ const newPoint = a === start ? b : a;
447
420
  polyline.unshift(newPoint);
448
421
  start = newPoint;
449
- } else {
450
- start = null;
451
- }
422
+ } else start = null;
452
423
  }
453
424
  while (end && segmentsMapIndexes[end]) {
454
- const nextLineIndex = (_b = segmentsMapIndexes[end]) == null ? void 0 : _b.find(
455
- (lineIndex) => !parsedSegmentIndexes.includes(lineIndex)
456
- );
457
- if (nextLineIndex) {
458
- parsedSegmentIndexes.push(nextLineIndex);
459
- const nextLineSegment = lineSegmentStrings[nextLineIndex];
460
- const nextLineSegmentPointIndex = nextLineSegment[0] === end ? 1 : 0;
461
- const newPoint = nextLineSegment[nextLineSegmentPointIndex];
425
+ const nextLineIndex = segmentsMapIndexes[end].find((li) => !parsedSegmentIndexes.has(li));
426
+ if (nextLineIndex !== void 0) {
427
+ parsedSegmentIndexes.add(nextLineIndex);
428
+ const [a, b] = lineSegmentStrings[nextLineIndex];
429
+ const newPoint = a === end ? b : a;
462
430
  polyline.push(newPoint);
463
431
  end = newPoint;
464
- } else {
465
- end = null;
466
- }
432
+ } else end = null;
467
433
  }
468
434
  polylines.push(polyline.map((coord) => coord.split(",").map((v) => parseFloat(v))));
469
435
  }
@@ -477,10 +443,10 @@ var contourElevations = (minElevation, maxElevation, interval) => {
477
443
  throw new Error(`No contour lines at interval: ${interval} between elevation ${minElevation} and ${maxElevation}`);
478
444
  }
479
445
  const elevations = [];
480
- let elevation = Math.ceil(minElevation / interval) * interval;
481
- while (elevation < maxElevation) {
482
- elevations.push(elevation);
483
- elevation += interval;
446
+ const firstStep = Math.ceil(minElevation / interval);
447
+ const lastStep = Math.ceil(maxElevation / interval) - 1;
448
+ for (let step = firstStep; step <= lastStep; step++) {
449
+ elevations.push(step * interval);
484
450
  }
485
451
  return elevations;
486
452
  };
@@ -490,31 +456,55 @@ var constructGeojson = (elevationData) => {
490
456
  return prev.concat(
491
457
  polylines.map((polyline) => ({
492
458
  type: "Feature",
493
- geometry: {
494
- type: "LineString",
495
- coordinates: polyline
496
- },
497
- properties: {
498
- z: elevation
499
- }
459
+ geometry: { type: "LineString", coordinates: polyline },
460
+ properties: { z: elevation }
500
461
  }))
501
462
  );
502
463
  }, []);
503
- return {
504
- type: "FeatureCollection",
505
- features
506
- };
464
+ return { type: "FeatureCollection", features };
507
465
  };
508
- var getContours = (data, interval = 2) => __async(null, null, function* () {
509
- const triangles = data.surfaceDefinition.faces.map(
510
- (face) => face.map((vert) => data.surfaceDefinition.points[vert])
511
- );
512
- const [minElevation, maxElevation] = data.surfaceDefinition.points.reduce(
513
- ([prevMin, prevMax], curr) => [Math.min(prevMin, curr[2]), Math.max(prevMax, curr[2])],
514
- [Infinity, -Infinity]
515
- );
466
+ var precomputeSurfaceData = (data) => {
467
+ const { points, faces } = data.surfaceDefinition;
468
+ let minElevation = Infinity;
469
+ let maxElevation = -Infinity;
470
+ for (const pt of points) {
471
+ if (pt[2] < minElevation) minElevation = pt[2];
472
+ if (pt[2] > maxElevation) maxElevation = pt[2];
473
+ }
474
+ const triangles = faces.map((face) => face.map((vert) => points[vert]));
475
+ return { triangles, minElevation, maxElevation };
476
+ };
477
+ var bucketTrianglesByElevation = (triangles, elevations, interval) => {
478
+ const trianglesByElevation = /* @__PURE__ */ new Map();
479
+ for (const e of elevations) trianglesByElevation.set(e, []);
480
+ for (const tri of triangles) {
481
+ const zMin = Math.min(tri[0][2], tri[1][2], tri[2][2]);
482
+ const zMax = Math.max(tri[0][2], tri[1][2], tri[2][2]);
483
+ const firstStep = Math.ceil(zMin / interval);
484
+ const lastStep = Math.floor(zMax / interval);
485
+ for (let step = firstStep; step <= lastStep; step++) {
486
+ const rounded = step * interval;
487
+ const bucket = trianglesByElevation.get(rounded);
488
+ if (bucket) bucket.push(tri);
489
+ }
490
+ }
491
+ return trianglesByElevation;
492
+ };
493
+ var getContours = (data, interval = 2, precomputed) => __async(null, null, function* () {
494
+ const { triangles, minElevation, maxElevation } = precomputed != null ? precomputed : precomputeSurfaceData(data);
516
495
  const elevations = contourElevations(minElevation, maxElevation, interval);
517
- const elevationPolylines = yield Promise.all(elevations.map((elevation) => contoursWorker.send({ triangles, elevation })));
496
+ const trianglesByElevation = bucketTrianglesByElevation(triangles, elevations, interval);
497
+ const elevationPolylines = yield Promise.all(
498
+ elevations.map(
499
+ (elevation) => {
500
+ var _a;
501
+ return contoursWorker.send({
502
+ triangles: (_a = trianglesByElevation.get(elevation)) != null ? _a : [],
503
+ elevation
504
+ });
505
+ }
506
+ )
507
+ );
518
508
  return constructGeojson(elevationPolylines);
519
509
  });
520
510
  var get_contours_default = getContours;
@@ -597,9 +587,41 @@ var reprojectGeoJson = (geojson, sourceProjection, targetProjection = "WGS84", k
597
587
  return geojson;
598
588
  };
599
589
  var reproject_geojson_default = reprojectGeoJson;
590
+
591
+ // src/public/to-glb-and-contours.ts
592
+ var toGlbAndContours = (landXmlString, contourInterval = 2, generateOutline = true, center = "auto", surfaceId = -1) => __async(null, null, function* () {
593
+ const requestedCenter = center === "origin" ? [0, 0] : center === "auto" ? void 0 : center;
594
+ const requestedParsedSurfaces = filter_by_surfaceId_default(yield parse_xml_default(landXmlString), surfaceId);
595
+ const results = yield Promise.all(
596
+ requestedParsedSurfaces.map((surface) => __async(null, null, function* () {
597
+ const precomputed = precomputeSurfaceData(surface);
598
+ const [{ glb, center: resolvedCenter }, geojson] = yield Promise.all([
599
+ get_glb_default(surface, requestedCenter),
600
+ get_contours_default(surface, contourInterval, precomputed)
601
+ ]);
602
+ if (generateOutline) {
603
+ const outlineGeojson = get_outline_default(surface);
604
+ geojson.features = [...geojson.features, ...outlineGeojson.features];
605
+ }
606
+ const _a = surface, { surfaceDefinition } = _a, rest = __objRest(_a, ["surfaceDefinition"]);
607
+ return __spreadProps(__spreadValues({}, rest), {
608
+ glb,
609
+ center: resolvedCenter,
610
+ download: () => {
611
+ download_glb_default(glb, surface.name.replace(/\.xml$/, `${JSON.stringify(resolvedCenter)}.glb`));
612
+ },
613
+ geojson
614
+ });
615
+ }))
616
+ );
617
+ return results;
618
+ });
619
+ var to_glb_and_contours_default = toGlbAndContours;
600
620
  // Annotate the CommonJS export names for ESM import in node:
601
621
  0 && (module.exports = {
622
+ precomputeSurfaceData,
602
623
  reprojectGeoJson,
603
624
  toGeojsonContours,
604
- toGlb
625
+ toGlb,
626
+ toGlbAndContours
605
627
  });