landxml 0.4.2 → 0.5.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,22 @@
1
1
  # landxml
2
2
 
3
+ ## 0.5.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 2d30d02: Fix: Edge case generating contour lines that are shared between multiple faces
8
+
9
+ ## 0.5.0
10
+
11
+ ### Minor Changes
12
+
13
+ - df95e64: Added ability to generate surface outline geojson
14
+
15
+ ### Patch Changes
16
+
17
+ - df95e64: LandXML Face with with "i" attribute equal to "1" will now be correctly ignored
18
+ - df95e64: Improved contour generation algorithm
19
+
3
20
  ## 0.4.2
4
21
 
5
22
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -39,7 +39,7 @@ declare const toGlb: (landXmlString: string, center?: "auto" | "origin" | [
39
39
  * @returns {string} surfaceContours[].wktString - WKT string of LandXML coordinate system projection
40
40
  * @returns {Object} surfaceContours[].geojson - Geojson feature collection of contour lines
41
41
  */
42
- declare const toGeojsonContours: (landXmlString: string, contourInterval?: number, surfaceId?: string | number) => Promise<{
42
+ declare const toGeojsonContours: (landXmlString: string, contourInterval?: number, generateOutline?: boolean, surfaceId?: string | number) => Promise<{
43
43
  name: string;
44
44
  description: string;
45
45
  sourceFile: string;
package/dist/index.d.ts CHANGED
@@ -39,7 +39,7 @@ declare const toGlb: (landXmlString: string, center?: "auto" | "origin" | [
39
39
  * @returns {string} surfaceContours[].wktString - WKT string of LandXML coordinate system projection
40
40
  * @returns {Object} surfaceContours[].geojson - Geojson feature collection of contour lines
41
41
  */
42
- declare const toGeojsonContours: (landXmlString: string, contourInterval?: number, surfaceId?: string | number) => Promise<{
42
+ declare const toGeojsonContours: (landXmlString: string, contourInterval?: number, generateOutline?: boolean, surfaceId?: string | number) => Promise<{
43
43
  name: string;
44
44
  description: string;
45
45
  sourceFile: string;
package/dist/index.js CHANGED
@@ -164,14 +164,16 @@ var parseXML = (xmlString) => __async(void 0, null, function* () {
164
164
  points.push([x, y, z]);
165
165
  pointIdMap[attr.id] = points.length - 1;
166
166
  }, []);
167
- faces = surface.Definition.Faces.F.map((face) => {
168
- const { content } = face;
167
+ faces = surface.Definition.Faces.F.reduce((faceList, face) => {
168
+ const { attr, content } = face;
169
+ if (attr.i === "1")
170
+ return faceList;
169
171
  const [a, b, c] = content.split(" ").map((v) => pointIdMap[v]);
170
172
  if ([a, b, c].filter((v) => typeof v === "undefined").length > 0) {
171
173
  throw `Invalid LandXML. A face is referencing a point that doesn't exist. Face is referencing points: ${content}`;
172
174
  }
173
- return [a, b, c];
174
- });
175
+ return faceList.concat([[a, b, c]]);
176
+ }, []);
175
177
  return {
176
178
  sourceFile: LandXML.Project.attr.name,
177
179
  timeStamp: LandXML.Application.attr.timeStamp,
@@ -216,15 +218,25 @@ var to_glb_default = toGlb;
216
218
 
217
219
  // src/private/get-contours.ts
218
220
  var contourLineOnFace = (face, z) => {
221
+ let vertsAtElevation = 0;
219
222
  let line = [];
220
223
  for (let i = 0; i < face.length; i++) {
221
224
  let vertex1 = face[i];
222
225
  let vertex2 = face[(i + 1) % face.length];
223
- if (vertex1[2] <= z && vertex2[2] >= z || vertex1[2] >= z && vertex2[2] <= z) {
226
+ if (vertex1[2] === z)
227
+ vertsAtElevation++;
228
+ if ((vertex1[2] <= z && vertex2[2] >= z || vertex1[2] >= z && vertex2[2] <= z) && !Number.isNaN((z - vertex1[2]) / (vertex2[2] - vertex1[2]))) {
224
229
  let t = (z - vertex1[2]) / (vertex2[2] - vertex1[2]);
225
230
  line.push([vertex1[0] + t * (vertex2[0] - vertex1[0]), vertex1[1] + t * (vertex2[1] - vertex1[1])]);
226
231
  }
227
232
  }
233
+ if (vertsAtElevation >= 2 && face.map((f) => f[2]).reduce((a, b) => a + b) > z * face.length)
234
+ return void 0;
235
+ if (line.length === 2 && line[0][0] === line[1][0] && line[0][1] === line[1][1])
236
+ return void 0;
237
+ if (line.length > 2) {
238
+ line = [...new Set(line.map((v) => JSON.stringify(v)))].map((s) => JSON.parse(s));
239
+ }
228
240
  return line.length > 0 ? line : void 0;
229
241
  };
230
242
  var linesToPolyLines = (lineSegments) => {
@@ -325,29 +337,80 @@ var getContours = (data, interval = 2) => __async(void 0, null, function* () {
325
337
  [Infinity, -Infinity]
326
338
  );
327
339
  const elevations = contourElevations(minElevation, maxElevation, interval);
328
- const elevationPolylines = elevations.map((e) => ({
329
- elevation: e,
330
- polylines: linesToPolyLines(
331
- triangles.reduce((prev, curr) => {
332
- const line = contourLineOnFace(curr, e);
333
- if (line)
334
- prev.push(line);
335
- return prev;
336
- }, [])
337
- )
338
- }));
340
+ const elevationPolylines = elevations.map((e) => {
341
+ const linesAtElevationE = triangles.reduce((prev, curr) => {
342
+ const line = contourLineOnFace(curr, e);
343
+ if (line)
344
+ prev.push(line);
345
+ return prev;
346
+ }, []);
347
+ const polylinesAtElevationE = linesToPolyLines(linesAtElevationE);
348
+ if (e === 442) {
349
+ console.log("linesAtElevationE", JSON.stringify(linesAtElevationE));
350
+ console.log("polylinesAtElevationE", JSON.stringify(polylinesAtElevationE));
351
+ }
352
+ return {
353
+ elevation: e,
354
+ polylines: polylinesAtElevationE
355
+ };
356
+ });
339
357
  return constructGeojson(elevationPolylines);
340
358
  });
341
359
  var get_contours_default = getContours;
342
360
 
361
+ // src/private/get-outline.ts
362
+ var getOutline = (surface) => {
363
+ const triangleVertexIdEdgePairs = [];
364
+ let i = -1;
365
+ let pairs = [];
366
+ surface.surfaceDefinition.faces.forEach((f) => {
367
+ pairs = [];
368
+ [
369
+ [f[0], f[1]],
370
+ [f[1], f[2]],
371
+ [f[2], f[0]]
372
+ ].forEach(([a, b]) => {
373
+ if (a < b) {
374
+ pairs.push(`${a};${b}`);
375
+ } else {
376
+ pairs.push(`${b};${a}`);
377
+ }
378
+ });
379
+ pairs.forEach((pair) => {
380
+ i = triangleVertexIdEdgePairs.indexOf(pair);
381
+ if (i >= 0) {
382
+ triangleVertexIdEdgePairs.splice(i, 1);
383
+ } else {
384
+ triangleVertexIdEdgePairs.push(pair);
385
+ }
386
+ });
387
+ });
388
+ const edges = [];
389
+ triangleVertexIdEdgePairs.map((pair) => {
390
+ const [v1, v2] = pair.split(";").map((v) => {
391
+ var _a;
392
+ return (_a = surface.surfaceDefinition.points[parseInt(v, 10)]) == null ? void 0 : _a.slice(0, 2);
393
+ });
394
+ if (v1 && v2)
395
+ edges.push([v1, v2]);
396
+ });
397
+ return constructGeojson([{ elevation: 0, polylines: linesToPolyLines(edges) }]);
398
+ };
399
+ var get_outline_default = getOutline;
400
+
343
401
  // src/public/to-geojson-contours.ts
344
- var toGeojsonContours = (landXmlString, contourInterval = 2, surfaceId = -1) => __async(void 0, null, function* () {
402
+ var toGeojsonContours = (landXmlString, contourInterval = 2, generateOutline = true, surfaceId = -1) => __async(void 0, null, function* () {
345
403
  let requestedParsedSurfaces = filter_by_surfaceId_default(yield parse_xml_default(landXmlString), surfaceId);
346
404
  const contours = yield Promise.all(
347
405
  requestedParsedSurfaces.map(
348
406
  (surface) => new Promise((resolve, reject) => __async(void 0, null, function* () {
349
407
  try {
350
408
  const geojson = yield get_contours_default(surface, contourInterval);
409
+ if (generateOutline) {
410
+ const outlineGeojson = get_outline_default(surface);
411
+ console.log(outlineGeojson.features);
412
+ geojson.features = [...geojson.features, ...outlineGeojson.features];
413
+ }
351
414
  const _a = surface, { surfaceDefinition } = _a, rest = __objRest(_a, ["surfaceDefinition"]);
352
415
  resolve(__spreadProps(__spreadValues({}, rest), {
353
416
  geojson
package/dist/index.mjs CHANGED
@@ -129,14 +129,16 @@ var parseXML = (xmlString) => __async(void 0, null, function* () {
129
129
  points.push([x, y, z]);
130
130
  pointIdMap[attr.id] = points.length - 1;
131
131
  }, []);
132
- faces = surface.Definition.Faces.F.map((face) => {
133
- const { content } = face;
132
+ faces = surface.Definition.Faces.F.reduce((faceList, face) => {
133
+ const { attr, content } = face;
134
+ if (attr.i === "1")
135
+ return faceList;
134
136
  const [a, b, c] = content.split(" ").map((v) => pointIdMap[v]);
135
137
  if ([a, b, c].filter((v) => typeof v === "undefined").length > 0) {
136
138
  throw `Invalid LandXML. A face is referencing a point that doesn't exist. Face is referencing points: ${content}`;
137
139
  }
138
- return [a, b, c];
139
- });
140
+ return faceList.concat([[a, b, c]]);
141
+ }, []);
140
142
  return {
141
143
  sourceFile: LandXML.Project.attr.name,
142
144
  timeStamp: LandXML.Application.attr.timeStamp,
@@ -181,15 +183,25 @@ var to_glb_default = toGlb;
181
183
 
182
184
  // src/private/get-contours.ts
183
185
  var contourLineOnFace = (face, z) => {
186
+ let vertsAtElevation = 0;
184
187
  let line = [];
185
188
  for (let i = 0; i < face.length; i++) {
186
189
  let vertex1 = face[i];
187
190
  let vertex2 = face[(i + 1) % face.length];
188
- if (vertex1[2] <= z && vertex2[2] >= z || vertex1[2] >= z && vertex2[2] <= z) {
191
+ if (vertex1[2] === z)
192
+ vertsAtElevation++;
193
+ if ((vertex1[2] <= z && vertex2[2] >= z || vertex1[2] >= z && vertex2[2] <= z) && !Number.isNaN((z - vertex1[2]) / (vertex2[2] - vertex1[2]))) {
189
194
  let t = (z - vertex1[2]) / (vertex2[2] - vertex1[2]);
190
195
  line.push([vertex1[0] + t * (vertex2[0] - vertex1[0]), vertex1[1] + t * (vertex2[1] - vertex1[1])]);
191
196
  }
192
197
  }
198
+ if (vertsAtElevation >= 2 && face.map((f) => f[2]).reduce((a, b) => a + b) > z * face.length)
199
+ return void 0;
200
+ if (line.length === 2 && line[0][0] === line[1][0] && line[0][1] === line[1][1])
201
+ return void 0;
202
+ if (line.length > 2) {
203
+ line = [...new Set(line.map((v) => JSON.stringify(v)))].map((s) => JSON.parse(s));
204
+ }
193
205
  return line.length > 0 ? line : void 0;
194
206
  };
195
207
  var linesToPolyLines = (lineSegments) => {
@@ -290,29 +302,80 @@ var getContours = (data, interval = 2) => __async(void 0, null, function* () {
290
302
  [Infinity, -Infinity]
291
303
  );
292
304
  const elevations = contourElevations(minElevation, maxElevation, interval);
293
- const elevationPolylines = elevations.map((e) => ({
294
- elevation: e,
295
- polylines: linesToPolyLines(
296
- triangles.reduce((prev, curr) => {
297
- const line = contourLineOnFace(curr, e);
298
- if (line)
299
- prev.push(line);
300
- return prev;
301
- }, [])
302
- )
303
- }));
305
+ const elevationPolylines = elevations.map((e) => {
306
+ const linesAtElevationE = triangles.reduce((prev, curr) => {
307
+ const line = contourLineOnFace(curr, e);
308
+ if (line)
309
+ prev.push(line);
310
+ return prev;
311
+ }, []);
312
+ const polylinesAtElevationE = linesToPolyLines(linesAtElevationE);
313
+ if (e === 442) {
314
+ console.log("linesAtElevationE", JSON.stringify(linesAtElevationE));
315
+ console.log("polylinesAtElevationE", JSON.stringify(polylinesAtElevationE));
316
+ }
317
+ return {
318
+ elevation: e,
319
+ polylines: polylinesAtElevationE
320
+ };
321
+ });
304
322
  return constructGeojson(elevationPolylines);
305
323
  });
306
324
  var get_contours_default = getContours;
307
325
 
326
+ // src/private/get-outline.ts
327
+ var getOutline = (surface) => {
328
+ const triangleVertexIdEdgePairs = [];
329
+ let i = -1;
330
+ let pairs = [];
331
+ surface.surfaceDefinition.faces.forEach((f) => {
332
+ pairs = [];
333
+ [
334
+ [f[0], f[1]],
335
+ [f[1], f[2]],
336
+ [f[2], f[0]]
337
+ ].forEach(([a, b]) => {
338
+ if (a < b) {
339
+ pairs.push(`${a};${b}`);
340
+ } else {
341
+ pairs.push(`${b};${a}`);
342
+ }
343
+ });
344
+ pairs.forEach((pair) => {
345
+ i = triangleVertexIdEdgePairs.indexOf(pair);
346
+ if (i >= 0) {
347
+ triangleVertexIdEdgePairs.splice(i, 1);
348
+ } else {
349
+ triangleVertexIdEdgePairs.push(pair);
350
+ }
351
+ });
352
+ });
353
+ const edges = [];
354
+ triangleVertexIdEdgePairs.map((pair) => {
355
+ const [v1, v2] = pair.split(";").map((v) => {
356
+ var _a;
357
+ return (_a = surface.surfaceDefinition.points[parseInt(v, 10)]) == null ? void 0 : _a.slice(0, 2);
358
+ });
359
+ if (v1 && v2)
360
+ edges.push([v1, v2]);
361
+ });
362
+ return constructGeojson([{ elevation: 0, polylines: linesToPolyLines(edges) }]);
363
+ };
364
+ var get_outline_default = getOutline;
365
+
308
366
  // src/public/to-geojson-contours.ts
309
- var toGeojsonContours = (landXmlString, contourInterval = 2, surfaceId = -1) => __async(void 0, null, function* () {
367
+ var toGeojsonContours = (landXmlString, contourInterval = 2, generateOutline = true, surfaceId = -1) => __async(void 0, null, function* () {
310
368
  let requestedParsedSurfaces = filter_by_surfaceId_default(yield parse_xml_default(landXmlString), surfaceId);
311
369
  const contours = yield Promise.all(
312
370
  requestedParsedSurfaces.map(
313
371
  (surface) => new Promise((resolve, reject) => __async(void 0, null, function* () {
314
372
  try {
315
373
  const geojson = yield get_contours_default(surface, contourInterval);
374
+ if (generateOutline) {
375
+ const outlineGeojson = get_outline_default(surface);
376
+ console.log(outlineGeojson.features);
377
+ geojson.features = [...geojson.features, ...outlineGeojson.features];
378
+ }
316
379
  const _a = surface, { surfaceDefinition } = _a, rest = __objRest(_a, ["surfaceDefinition"]);
317
380
  resolve(__spreadProps(__spreadValues({}, rest), {
318
381
  geojson
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "landxml",
3
- "version": "0.4.2",
3
+ "version": "0.5.1",
4
4
  "description": "Parse LandXML surfaces on the modern web.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",