landxml 0.4.2 → 0.5.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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # landxml
2
2
 
3
+ ## 0.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - df95e64: Added ability to generate surface outline geojson
8
+
9
+ ### Patch Changes
10
+
11
+ - df95e64: LandXML Face with with "i" attribute equal to "1" will now be correctly ignored
12
+ - df95e64: Improved contour generation algorithm
13
+
3
14
  ## 0.4.2
4
15
 
5
16
  ### 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,
@@ -220,11 +222,14 @@ var contourLineOnFace = (face, z) => {
220
222
  for (let i = 0; i < face.length; i++) {
221
223
  let vertex1 = face[i];
222
224
  let vertex2 = face[(i + 1) % face.length];
223
- if (vertex1[2] <= z && vertex2[2] >= z || vertex1[2] >= z && vertex2[2] <= z) {
225
+ if ((vertex1[2] <= z && vertex2[2] >= z || vertex1[2] >= z && vertex2[2] <= z) && !Number.isNaN((z - vertex1[2]) / (vertex2[2] - vertex1[2]))) {
224
226
  let t = (z - vertex1[2]) / (vertex2[2] - vertex1[2]);
225
227
  line.push([vertex1[0] + t * (vertex2[0] - vertex1[0]), vertex1[1] + t * (vertex2[1] - vertex1[1])]);
226
228
  }
227
229
  }
230
+ if (line.length > 2) {
231
+ line = [...new Set(line.map((v) => JSON.stringify(v)))].map((s) => JSON.parse(s));
232
+ }
228
233
  return line.length > 0 ? line : void 0;
229
234
  };
230
235
  var linesToPolyLines = (lineSegments) => {
@@ -325,29 +330,76 @@ var getContours = (data, interval = 2) => __async(void 0, null, function* () {
325
330
  [Infinity, -Infinity]
326
331
  );
327
332
  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
- }));
333
+ const elevationPolylines = elevations.map((e) => {
334
+ const linesAtElevationE = triangles.reduce((prev, curr) => {
335
+ const line = contourLineOnFace(curr, e);
336
+ if (line)
337
+ prev.push(line);
338
+ return prev;
339
+ }, []);
340
+ const polylinesAtElevationE = linesToPolyLines(linesAtElevationE);
341
+ return {
342
+ elevation: e,
343
+ polylines: polylinesAtElevationE
344
+ };
345
+ });
339
346
  return constructGeojson(elevationPolylines);
340
347
  });
341
348
  var get_contours_default = getContours;
342
349
 
350
+ // src/private/get-outline.ts
351
+ var getOutline = (surface) => {
352
+ const triangleVertexIdEdgePairs = [];
353
+ let i = -1;
354
+ let pairs = [];
355
+ surface.surfaceDefinition.faces.forEach((f) => {
356
+ pairs = [];
357
+ [
358
+ [f[0], f[1]],
359
+ [f[1], f[2]],
360
+ [f[2], f[0]]
361
+ ].forEach(([a, b]) => {
362
+ if (a < b) {
363
+ pairs.push(`${a};${b}`);
364
+ } else {
365
+ pairs.push(`${b};${a}`);
366
+ }
367
+ });
368
+ pairs.forEach((pair) => {
369
+ i = triangleVertexIdEdgePairs.indexOf(pair);
370
+ if (i >= 0) {
371
+ triangleVertexIdEdgePairs.splice(i, 1);
372
+ } else {
373
+ triangleVertexIdEdgePairs.push(pair);
374
+ }
375
+ });
376
+ });
377
+ const edges = [];
378
+ triangleVertexIdEdgePairs.map((pair) => {
379
+ const [v1, v2] = pair.split(";").map((v) => {
380
+ var _a;
381
+ return (_a = surface.surfaceDefinition.points[parseInt(v, 10)]) == null ? void 0 : _a.slice(0, 2);
382
+ });
383
+ if (v1 && v2)
384
+ edges.push([v1, v2]);
385
+ });
386
+ return constructGeojson([{ elevation: 0, polylines: linesToPolyLines(edges) }]);
387
+ };
388
+ var get_outline_default = getOutline;
389
+
343
390
  // src/public/to-geojson-contours.ts
344
- var toGeojsonContours = (landXmlString, contourInterval = 2, surfaceId = -1) => __async(void 0, null, function* () {
391
+ var toGeojsonContours = (landXmlString, contourInterval = 2, generateOutline = true, surfaceId = -1) => __async(void 0, null, function* () {
345
392
  let requestedParsedSurfaces = filter_by_surfaceId_default(yield parse_xml_default(landXmlString), surfaceId);
346
393
  const contours = yield Promise.all(
347
394
  requestedParsedSurfaces.map(
348
395
  (surface) => new Promise((resolve, reject) => __async(void 0, null, function* () {
349
396
  try {
350
397
  const geojson = yield get_contours_default(surface, contourInterval);
398
+ if (generateOutline) {
399
+ const outlineGeojson = get_outline_default(surface);
400
+ console.log(outlineGeojson.features);
401
+ geojson.features = [...geojson.features, ...outlineGeojson.features];
402
+ }
351
403
  const _a = surface, { surfaceDefinition } = _a, rest = __objRest(_a, ["surfaceDefinition"]);
352
404
  resolve(__spreadProps(__spreadValues({}, rest), {
353
405
  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,
@@ -185,11 +187,14 @@ var contourLineOnFace = (face, z) => {
185
187
  for (let i = 0; i < face.length; i++) {
186
188
  let vertex1 = face[i];
187
189
  let vertex2 = face[(i + 1) % face.length];
188
- if (vertex1[2] <= z && vertex2[2] >= z || vertex1[2] >= z && vertex2[2] <= z) {
190
+ if ((vertex1[2] <= z && vertex2[2] >= z || vertex1[2] >= z && vertex2[2] <= z) && !Number.isNaN((z - vertex1[2]) / (vertex2[2] - vertex1[2]))) {
189
191
  let t = (z - vertex1[2]) / (vertex2[2] - vertex1[2]);
190
192
  line.push([vertex1[0] + t * (vertex2[0] - vertex1[0]), vertex1[1] + t * (vertex2[1] - vertex1[1])]);
191
193
  }
192
194
  }
195
+ if (line.length > 2) {
196
+ line = [...new Set(line.map((v) => JSON.stringify(v)))].map((s) => JSON.parse(s));
197
+ }
193
198
  return line.length > 0 ? line : void 0;
194
199
  };
195
200
  var linesToPolyLines = (lineSegments) => {
@@ -290,29 +295,76 @@ var getContours = (data, interval = 2) => __async(void 0, null, function* () {
290
295
  [Infinity, -Infinity]
291
296
  );
292
297
  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
- }));
298
+ const elevationPolylines = elevations.map((e) => {
299
+ const linesAtElevationE = triangles.reduce((prev, curr) => {
300
+ const line = contourLineOnFace(curr, e);
301
+ if (line)
302
+ prev.push(line);
303
+ return prev;
304
+ }, []);
305
+ const polylinesAtElevationE = linesToPolyLines(linesAtElevationE);
306
+ return {
307
+ elevation: e,
308
+ polylines: polylinesAtElevationE
309
+ };
310
+ });
304
311
  return constructGeojson(elevationPolylines);
305
312
  });
306
313
  var get_contours_default = getContours;
307
314
 
315
+ // src/private/get-outline.ts
316
+ var getOutline = (surface) => {
317
+ const triangleVertexIdEdgePairs = [];
318
+ let i = -1;
319
+ let pairs = [];
320
+ surface.surfaceDefinition.faces.forEach((f) => {
321
+ pairs = [];
322
+ [
323
+ [f[0], f[1]],
324
+ [f[1], f[2]],
325
+ [f[2], f[0]]
326
+ ].forEach(([a, b]) => {
327
+ if (a < b) {
328
+ pairs.push(`${a};${b}`);
329
+ } else {
330
+ pairs.push(`${b};${a}`);
331
+ }
332
+ });
333
+ pairs.forEach((pair) => {
334
+ i = triangleVertexIdEdgePairs.indexOf(pair);
335
+ if (i >= 0) {
336
+ triangleVertexIdEdgePairs.splice(i, 1);
337
+ } else {
338
+ triangleVertexIdEdgePairs.push(pair);
339
+ }
340
+ });
341
+ });
342
+ const edges = [];
343
+ triangleVertexIdEdgePairs.map((pair) => {
344
+ const [v1, v2] = pair.split(";").map((v) => {
345
+ var _a;
346
+ return (_a = surface.surfaceDefinition.points[parseInt(v, 10)]) == null ? void 0 : _a.slice(0, 2);
347
+ });
348
+ if (v1 && v2)
349
+ edges.push([v1, v2]);
350
+ });
351
+ return constructGeojson([{ elevation: 0, polylines: linesToPolyLines(edges) }]);
352
+ };
353
+ var get_outline_default = getOutline;
354
+
308
355
  // src/public/to-geojson-contours.ts
309
- var toGeojsonContours = (landXmlString, contourInterval = 2, surfaceId = -1) => __async(void 0, null, function* () {
356
+ var toGeojsonContours = (landXmlString, contourInterval = 2, generateOutline = true, surfaceId = -1) => __async(void 0, null, function* () {
310
357
  let requestedParsedSurfaces = filter_by_surfaceId_default(yield parse_xml_default(landXmlString), surfaceId);
311
358
  const contours = yield Promise.all(
312
359
  requestedParsedSurfaces.map(
313
360
  (surface) => new Promise((resolve, reject) => __async(void 0, null, function* () {
314
361
  try {
315
362
  const geojson = yield get_contours_default(surface, contourInterval);
363
+ if (generateOutline) {
364
+ const outlineGeojson = get_outline_default(surface);
365
+ console.log(outlineGeojson.features);
366
+ geojson.features = [...geojson.features, ...outlineGeojson.features];
367
+ }
316
368
  const _a = surface, { surfaceDefinition } = _a, rest = __objRest(_a, ["surfaceDefinition"]);
317
369
  resolve(__spreadProps(__spreadValues({}, rest), {
318
370
  geojson
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "landxml",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Parse LandXML surfaces on the modern web.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",