landxml 0.7.1 → 0.8.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # landxml
2
2
 
3
+ ## 0.8.1
4
+
5
+ ### Patch Changes
6
+
7
+ - e886c37: Return empty geojson and console.warn rather than throwing error when generating contours.
8
+
9
+ ## 0.8.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 6a4a531: Modified "auto" behavior on LandXML files with multiple surfaces to create a combined center (rather than each surface its own cetner)
14
+
3
15
  ## 0.7.1
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -19,8 +19,6 @@ Easily transform LandXML surfaces into GeoJSON contours or GLB 3D models for use
19
19
  npm install landxml
20
20
  ```
21
21
 
22
- ---
23
-
24
22
  ## Examples
25
23
 
26
24
  ### Get contours and a GLB model together (recommended)
@@ -36,7 +34,7 @@ const surfaces = await toGlbAndContours(
36
34
  landXmlString,
37
35
  2, // contour interval (LandXML units)
38
36
  true, // generate outline
39
- "auto", // GLB centre: "auto" | "origin" | [x, y]
37
+ "auto", // GLB center: "auto" | "origin" | [x, y]
40
38
  );
41
39
 
42
40
  const { glb, center, geojson, wktString, download } = surfaces[0];
@@ -59,7 +57,7 @@ const landXmlString = `<?xml version="1.0"?>...<LandXML>...</LandXML>`;
59
57
 
60
58
  const surfaces = await toGeojsonContours(
61
59
  landXmlString,
62
- 2, // contour interval (metres)
60
+ 2, // contour interval (LandXML units)
63
61
  true, // include surface outline as a z=0 feature
64
62
  );
65
63
 
@@ -186,3 +184,13 @@ Returns `Promise<{ name, description, sourceFile, timeStamp, glb, center, downlo
186
184
  | `keepOriginalGeometry` | `boolean` | `true` | Store original coordinates under `feature.properties._rawGeometry` |
187
185
 
188
186
  Returns the mutated `FeatureCollection` with updated coordinates.
187
+
188
+ ---
189
+
190
+ ## Multi-surface center behaviour
191
+
192
+ When a LandXML file contains multiple surfaces and `center` is set to `"auto"` (the default), the package computes a **single shared median center** across all surfaces' points. Every GLB produced in that call is offset by the same origin, so the surfaces remain correctly positioned relative to each other in your 3D scene. This happens automatically — no extra configuration is needed.
193
+
194
+ If you need each surface to be individually centered (e.g. you are processing them in isolation), pass an explicit `[x, y]` pair instead.
195
+
196
+ ---
package/dist/index.d.mts CHANGED
@@ -3,7 +3,9 @@ import { FeatureCollection, LineString } from 'geojson';
3
3
 
4
4
  /**
5
5
  * @param landXmlString
6
- * @param center 3D models don't work well when they're far from origin (coordinate `[0,0,0]`), therefore by default your center is moved to the median of x and y axis of your surface
6
+ * @param center 3D models don't work well when they're far from origin (coordinate `[0,0,0]`), therefore by default your center is moved to the median of x and y axis of your surface.
7
+ * When processing multiple surfaces with `"auto"`, a **single shared center** is computed
8
+ * from all surfaces combined so they remain correctly positioned relative to each other.
7
9
  * @param surfaceId Surface name or index if your LandXML contains multiple surfaces. By default all surfaces are converted and returns an array of glb Uint8Array
8
10
  * @returns {Object[]} glbs - Array of processed glbs (will also return an array when just one surface has been processed)
9
11
  * @returns {string} glbs[].name - Name of surface as defined in LandXML
@@ -11,7 +13,7 @@ import { FeatureCollection, LineString } from 'geojson';
11
13
  * @returns {string} glbs[].sourceFile - Source file of where the LandXML was generated from.
12
14
  * @returns {string} glbs[].timeStamp - Timestamp of when the surface was exported into LandXML
13
15
  * @returns {Uint8Array} glbs[].glb - Uint8Array binary data of glb
14
- * @returns {[number, number]} glbs[].center - Offset center of the processed glb
16
+ * @returns {[number, number]} glbs[].center - Shared offset center applied to all GLBs
15
17
  * @returns {function(): void} glbs[].download - Convenient way to download the GLB, within the filename the new center will be appended.
16
18
  */
17
19
  declare const toGlb: (landXmlString: string, center?: "auto" | "origin" | [
@@ -67,7 +69,7 @@ type GlbAndContoursResult = {
67
69
  wktString?: string;
68
70
  /** Binary GLB data */
69
71
  glb: Uint8Array;
70
- /** XY center used to offset the GLB model from origin */
72
+ /** Shared XY center used to offset all GLB models from origin */
71
73
  center: [x: number, y: number];
72
74
  /** Convenience download trigger (browser only) */
73
75
  download: () => void;
@@ -84,12 +86,16 @@ type GlbAndContoursResult = {
84
86
  * Use this instead of calling `toGlb` + `toGeojsonContours` separately when
85
87
  * you need both outputs, as it eliminates all redundant work.
86
88
  *
89
+ * When processing multiple surfaces with `center: "auto"`, a **single shared
90
+ * center** is computed from all surfaces combined, so every GLB is offset by
91
+ * the same origin and surfaces remain correctly positioned relative to each other.
92
+ *
87
93
  * @param landXmlString Raw LandXML string
88
94
  * @param contourInterval Vertical interval between contour lines (default 2)
89
95
  * @param generateOutline When true, the outline of each surface is appended to
90
96
  * the GeoJSON as a z=0 feature (default true)
91
- * @param center GLB origin strategy: "auto" (median XY), "origin" ([0,0]),
92
- * or an explicit [x, y] pair (default "auto")
97
+ * @param center GLB origin strategy: "auto" (combined median XY of all surfaces),
98
+ * "origin" ([0,0]), or an explicit [x, y] pair (default "auto")
93
99
  * @param surfaceId Surface name or 0-based index to process a single
94
100
  * surface; -1 processes all surfaces (default -1)
95
101
  */
package/dist/index.d.ts CHANGED
@@ -3,7 +3,9 @@ import { FeatureCollection, LineString } from 'geojson';
3
3
 
4
4
  /**
5
5
  * @param landXmlString
6
- * @param center 3D models don't work well when they're far from origin (coordinate `[0,0,0]`), therefore by default your center is moved to the median of x and y axis of your surface
6
+ * @param center 3D models don't work well when they're far from origin (coordinate `[0,0,0]`), therefore by default your center is moved to the median of x and y axis of your surface.
7
+ * When processing multiple surfaces with `"auto"`, a **single shared center** is computed
8
+ * from all surfaces combined so they remain correctly positioned relative to each other.
7
9
  * @param surfaceId Surface name or index if your LandXML contains multiple surfaces. By default all surfaces are converted and returns an array of glb Uint8Array
8
10
  * @returns {Object[]} glbs - Array of processed glbs (will also return an array when just one surface has been processed)
9
11
  * @returns {string} glbs[].name - Name of surface as defined in LandXML
@@ -11,7 +13,7 @@ import { FeatureCollection, LineString } from 'geojson';
11
13
  * @returns {string} glbs[].sourceFile - Source file of where the LandXML was generated from.
12
14
  * @returns {string} glbs[].timeStamp - Timestamp of when the surface was exported into LandXML
13
15
  * @returns {Uint8Array} glbs[].glb - Uint8Array binary data of glb
14
- * @returns {[number, number]} glbs[].center - Offset center of the processed glb
16
+ * @returns {[number, number]} glbs[].center - Shared offset center applied to all GLBs
15
17
  * @returns {function(): void} glbs[].download - Convenient way to download the GLB, within the filename the new center will be appended.
16
18
  */
17
19
  declare const toGlb: (landXmlString: string, center?: "auto" | "origin" | [
@@ -67,7 +69,7 @@ type GlbAndContoursResult = {
67
69
  wktString?: string;
68
70
  /** Binary GLB data */
69
71
  glb: Uint8Array;
70
- /** XY center used to offset the GLB model from origin */
72
+ /** Shared XY center used to offset all GLB models from origin */
71
73
  center: [x: number, y: number];
72
74
  /** Convenience download trigger (browser only) */
73
75
  download: () => void;
@@ -84,12 +86,16 @@ type GlbAndContoursResult = {
84
86
  * Use this instead of calling `toGlb` + `toGeojsonContours` separately when
85
87
  * you need both outputs, as it eliminates all redundant work.
86
88
  *
89
+ * When processing multiple surfaces with `center: "auto"`, a **single shared
90
+ * center** is computed from all surfaces combined, so every GLB is offset by
91
+ * the same origin and surfaces remain correctly positioned relative to each other.
92
+ *
87
93
  * @param landXmlString Raw LandXML string
88
94
  * @param contourInterval Vertical interval between contour lines (default 2)
89
95
  * @param generateOutline When true, the outline of each surface is appended to
90
96
  * the GeoJSON as a z=0 feature (default true)
91
- * @param center GLB origin strategy: "auto" (median XY), "origin" ([0,0]),
92
- * or an explicit [x, y] pair (default "auto")
97
+ * @param center GLB origin strategy: "auto" (combined median XY of all surfaces),
98
+ * "origin" ([0,0]), or an explicit [x, y] pair (default "auto")
93
99
  * @param surfaceId Surface name or 0-based index to process a single
94
100
  * surface; -1 processes all surfaces (default -1)
95
101
  */
package/dist/index.js CHANGED
@@ -287,19 +287,27 @@ var parse_xml_default = parseXML;
287
287
 
288
288
  // src/public/to-glb.ts
289
289
  var toGlb = (landXmlString, center = "auto", surfaceId = -1) => __async(null, null, function* () {
290
- const requestedCenter = center == "origin" ? [0, 0] : center === "auto" ? void 0 : center;
291
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
+ }
292
300
  const glbs = yield Promise.all(
293
301
  requestedParsedSurfaces.map(
294
302
  (surface) => new Promise((resolve, reject) => __async(null, null, function* () {
295
303
  try {
296
- const { glb, center: center2 } = yield get_glb_default(surface, requestedCenter);
304
+ const { glb } = yield get_glb_default(surface, resolvedCenter);
297
305
  const _a = surface, { surfaceDefinition } = _a, rest = __objRest(_a, ["surfaceDefinition"]);
298
306
  resolve(__spreadProps(__spreadValues({}, rest), {
299
307
  glb,
300
- center: center2,
308
+ center: resolvedCenter,
301
309
  download: () => {
302
- download_glb_default(glb, surface.name.replace(/\.xml$/, `${JSON.stringify(center2)}.glb`));
310
+ download_glb_default(glb, surface.name.replace(/\.xml$/, `${JSON.stringify(resolvedCenter)}.glb`));
303
311
  }
304
312
  }));
305
313
  } catch (e) {
@@ -440,7 +448,8 @@ var contourElevations = (minElevation, maxElevation, interval) => {
440
448
  throw new Error("Contour elevations have to be finite numbers");
441
449
  }
442
450
  if (minElevation + interval > maxElevation) {
443
- throw new Error(`No contour lines at interval: ${interval} between elevation ${minElevation} and ${maxElevation}`);
451
+ console.warn(`No contour lines at interval: ${interval} between elevation ${minElevation} and ${maxElevation}`);
452
+ return [];
444
453
  }
445
454
  const elevations = [];
446
455
  const firstStep = Math.ceil(minElevation / interval);
@@ -493,6 +502,7 @@ var bucketTrianglesByElevation = (triangles, elevations, interval) => {
493
502
  var getContours = (data, interval = 2, precomputed) => __async(null, null, function* () {
494
503
  const { triangles, minElevation, maxElevation } = precomputed != null ? precomputed : precomputeSurfaceData(data);
495
504
  const elevations = contourElevations(minElevation, maxElevation, interval);
505
+ if (elevations.length === 0) return constructGeojson([]);
496
506
  const trianglesByElevation = bucketTrianglesByElevation(triangles, elevations, interval);
497
507
  const elevationPolylines = yield Promise.all(
498
508
  elevations.map(
@@ -590,13 +600,21 @@ var reproject_geojson_default = reprojectGeoJson;
590
600
 
591
601
  // src/public/to-glb-and-contours.ts
592
602
  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
603
  const requestedParsedSurfaces = filter_by_surfaceId_default(yield parse_xml_default(landXmlString), surfaceId);
604
+ let resolvedCenter;
605
+ if (center === "origin") {
606
+ resolvedCenter = [0, 0];
607
+ } else if (center === "auto") {
608
+ const allPoints = requestedParsedSurfaces.flatMap((s) => s.surfaceDefinition.points);
609
+ resolvedCenter = findXYAxisMedians(allPoints);
610
+ } else {
611
+ resolvedCenter = center;
612
+ }
595
613
  const results = yield Promise.all(
596
614
  requestedParsedSurfaces.map((surface) => __async(null, null, function* () {
597
615
  const precomputed = precomputeSurfaceData(surface);
598
- const [{ glb, center: resolvedCenter }, geojson] = yield Promise.all([
599
- get_glb_default(surface, requestedCenter),
616
+ const [{ glb }, geojson] = yield Promise.all([
617
+ get_glb_default(surface, resolvedCenter),
600
618
  get_contours_default(surface, contourInterval, precomputed)
601
619
  ]);
602
620
  if (generateOutline) {
package/dist/index.mjs CHANGED
@@ -250,19 +250,27 @@ var parse_xml_default = parseXML;
250
250
 
251
251
  // src/public/to-glb.ts
252
252
  var toGlb = (landXmlString, center = "auto", surfaceId = -1) => __async(null, null, function* () {
253
- const requestedCenter = center == "origin" ? [0, 0] : center === "auto" ? void 0 : center;
254
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
+ }
255
263
  const glbs = yield Promise.all(
256
264
  requestedParsedSurfaces.map(
257
265
  (surface) => new Promise((resolve, reject) => __async(null, null, function* () {
258
266
  try {
259
- const { glb, center: center2 } = yield get_glb_default(surface, requestedCenter);
267
+ const { glb } = yield get_glb_default(surface, resolvedCenter);
260
268
  const _a = surface, { surfaceDefinition } = _a, rest = __objRest(_a, ["surfaceDefinition"]);
261
269
  resolve(__spreadProps(__spreadValues({}, rest), {
262
270
  glb,
263
- center: center2,
271
+ center: resolvedCenter,
264
272
  download: () => {
265
- download_glb_default(glb, surface.name.replace(/\.xml$/, `${JSON.stringify(center2)}.glb`));
273
+ download_glb_default(glb, surface.name.replace(/\.xml$/, `${JSON.stringify(resolvedCenter)}.glb`));
266
274
  }
267
275
  }));
268
276
  } catch (e) {
@@ -403,7 +411,8 @@ var contourElevations = (minElevation, maxElevation, interval) => {
403
411
  throw new Error("Contour elevations have to be finite numbers");
404
412
  }
405
413
  if (minElevation + interval > maxElevation) {
406
- throw new Error(`No contour lines at interval: ${interval} between elevation ${minElevation} and ${maxElevation}`);
414
+ console.warn(`No contour lines at interval: ${interval} between elevation ${minElevation} and ${maxElevation}`);
415
+ return [];
407
416
  }
408
417
  const elevations = [];
409
418
  const firstStep = Math.ceil(minElevation / interval);
@@ -456,6 +465,7 @@ var bucketTrianglesByElevation = (triangles, elevations, interval) => {
456
465
  var getContours = (data, interval = 2, precomputed) => __async(null, null, function* () {
457
466
  const { triangles, minElevation, maxElevation } = precomputed != null ? precomputed : precomputeSurfaceData(data);
458
467
  const elevations = contourElevations(minElevation, maxElevation, interval);
468
+ if (elevations.length === 0) return constructGeojson([]);
459
469
  const trianglesByElevation = bucketTrianglesByElevation(triangles, elevations, interval);
460
470
  const elevationPolylines = yield Promise.all(
461
471
  elevations.map(
@@ -553,13 +563,21 @@ var reproject_geojson_default = reprojectGeoJson;
553
563
 
554
564
  // src/public/to-glb-and-contours.ts
555
565
  var toGlbAndContours = (landXmlString, contourInterval = 2, generateOutline = true, center = "auto", surfaceId = -1) => __async(null, null, function* () {
556
- const requestedCenter = center === "origin" ? [0, 0] : center === "auto" ? void 0 : center;
557
566
  const requestedParsedSurfaces = filter_by_surfaceId_default(yield parse_xml_default(landXmlString), surfaceId);
567
+ let resolvedCenter;
568
+ if (center === "origin") {
569
+ resolvedCenter = [0, 0];
570
+ } else if (center === "auto") {
571
+ const allPoints = requestedParsedSurfaces.flatMap((s) => s.surfaceDefinition.points);
572
+ resolvedCenter = findXYAxisMedians(allPoints);
573
+ } else {
574
+ resolvedCenter = center;
575
+ }
558
576
  const results = yield Promise.all(
559
577
  requestedParsedSurfaces.map((surface) => __async(null, null, function* () {
560
578
  const precomputed = precomputeSurfaceData(surface);
561
- const [{ glb, center: resolvedCenter }, geojson] = yield Promise.all([
562
- get_glb_default(surface, requestedCenter),
579
+ const [{ glb }, geojson] = yield Promise.all([
580
+ get_glb_default(surface, resolvedCenter),
563
581
  get_contours_default(surface, contourInterval, precomputed)
564
582
  ]);
565
583
  if (generateOutline) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "landxml",
3
- "version": "0.7.1",
3
+ "version": "0.8.1",
4
4
  "description": "Parse LandXML surfaces on the modern web.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",