GeoGMLer

GeoGMLer is a JavaScript library for converting GML data into GeoJSON. It translates FeatureMembers with Points, LineStrings, and Polygons, handling coordinates via gml:coordinates and gml:posList. Supports multi-geometries to ensure conversion to GeoJSON's FeatureCollection.

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/526229/1537672/GeoGMLer.js

  1. // ==UserScript==
  2. // @name GeoGMLer
  3. // @namespace https://github.com/JS55CT
  4. // @description GeoGMLer is a JavaScript library for converting GML data into GeoJSON. It translates FeatureMembers with Points, LineStrings, and Polygons, handling coordinates via gml:coordinates and gml:posList. Supports multi-geometries to ensure conversion to GeoJSON's FeatureCollection.
  5. // @version 2.1.0
  6. // @author JS55CT
  7. // @license MIT
  8. // @match *://this-library-is-not-supposed-to-run.com/*
  9. // ==/UserScript==
  10.  
  11. /***********************************************************
  12. * ## Project Home < https://github.com/JS55CT/GeoGMLer >
  13. * MIT License
  14. * Copyright (c) 2025 Justin
  15. * Permission is hereby granted, free of charge, to any person obtaining a copy
  16. * of this software and associated documentation files (the "Software"), to deal
  17. * in the Software without restriction, including without limitation the rights
  18. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  19. * copies of the Software, and to permit persons to whom the Software is
  20. * furnished to do so, subject to the following conditions:
  21. *
  22. * The above copyright notice and this permission notice shall be included in all
  23. * copies or substantial portions of the Software.
  24. *
  25. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  26. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  27. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  28. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  29. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  30. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31. * SOFTWARE.
  32. *
  33. * - Project was inspired by the work of [gml2geojson](https://github.com/deyihu/gml2geojson) (MIT licensed)
  34. * and builds upon the concepts and implementations found there
  35. **************************************************************/
  36.  
  37. /** TO DO: intergrate pro4.js to convert to ESPG:4326 standerd
  38. * The default Coordinate Reference System (CRS) for GeoJSON is WGS 84, which is represented by the EPSG code 4326.
  39. * This means that coordinates in a GeoJSON file are expected to be in longitude and latitude format, following the WGS 84 datum.
  40. * In the GeoJSON format, the coordinates are typically ordered as [longitude, latitude].
  41. * It's important to adhere to this order to ensure proper geospatial data interpretation and interoperability with GIS tools
  42. * and applications that conform to the GeoJSON specification.
  43. *
  44. * While GeoJSON does allow specifying other coordinate reference systems through extensions,
  45. * the use of any CRS other than WGS 84 is not recommended as it breaks the convention and could impact interoperability
  46. * and usability across services and applications that expect WGS 84.
  47. */
  48.  
  49. var GeoGMLer = (function () {
  50. /**
  51. * GeoGMLer constructor function.
  52. * @returns {GeoGMLer} - An instance of GeoGMLer.
  53. */
  54. function GeoGMLer(obj) {
  55. if (obj instanceof GeoGMLer) return obj;
  56. if (!(this instanceof GeoGMLer)) return new GeoGMLer(obj);
  57. this._wrapped = obj;
  58. }
  59.  
  60. const GEONODENAMES = ["geometryproperty", "geometryProperty"];
  61.  
  62. /**
  63. * Reads a GML string and prepares it for conversion by extracting
  64. * both the parsed XML document and its coordinate reference system (CRS).
  65. * @param {string} str - The GML string to read.
  66. * @returns {Object} - An object containing the parsed XML document and CRS name.
  67. * @property {Document} xmlDoc - The parsed XML document.
  68. * @property {string} crsName - The name of the coordinate reference system extracted from the GML.
  69. */
  70. GeoGMLer.prototype.read = function (gmlText) {
  71. const parser = new DOMParser();
  72. const xmlDoc = parser.parseFromString(gmlText, "application/xml");
  73.  
  74. // Check for parsing errors by looking for parser error tags
  75. const parseErrors = xmlDoc.getElementsByTagName("parsererror");
  76. if (parseErrors.length > 0) {
  77. const errorMessages = Array.from(parseErrors)
  78. .map((errorElement, index) => {
  79. return `Parsing Error ${index + 1}: ${errorElement.textContent}`;
  80. })
  81. .join("\n");
  82.  
  83. console.error(errorMessages);
  84. throw new Error("Failed to parse GML. See console for details.");
  85. }
  86.  
  87. // Extract the CRS directly within the read function if parsing is successful
  88. const crsName = this.getCRS(xmlDoc);
  89.  
  90. // Return both the XML document and the CRS
  91. return {
  92. xmlDoc,
  93. crsName,
  94. };
  95. };
  96.  
  97. /**
  98. * Converts a parsed GML XML document to a GeoJSON object, incorporating the specified CRS.
  99. * @param {Object} params - The parameters required for conversion.
  100. * @param {Document} params.xmlDoc - The parsed XML document to convert.
  101. * @param {string} params.crsName - The name of the coordinate reference system.
  102. * @returns {Object} - The GeoJSON object representing the features and their CRS.
  103. *
  104. * WARNING: The input GML geometries may specify a spatial reference system (SRS) through the `srsName` attribute.
  105. * This function extracts the `srsName` and includes it in the GeoJSON output under the 'crs' property:
  106. *
  107. * crs: {
  108. * type: "name",
  109. * properties: {
  110. * name: crsName,
  111. * },
  112. * },
  113. *
  114. * However, the function does not transform the coordinate values to match the EPSG:4326 (WGS 84) geoJSON standard.
  115. * This means the coordinate values remain in the original SRS specified by `srsName`.
  116. * Users should be aware that the GeoJSON output may not conform to expected standards if the original SRS
  117. * is not compatible with their intended use. It is essential to handle coordinate transformations as needed
  118. * for accurate spatial data representation.
  119. */
  120. GeoGMLer.prototype.toGeoJSON = function ({ xmlDoc, crsName }) {
  121. const geojson = {
  122. type: "FeatureCollection",
  123. features: [],
  124. crs: {
  125. type: "name",
  126. properties: {
  127. name: crsName,
  128. },
  129. },
  130. };
  131.  
  132. // Get the main element of the feature collection
  133. const featureCollectionEle = xmlDoc.children[0];
  134.  
  135. // Check if the node is a FeatureCollection, considering possible namespace prefixes
  136. const nodeName = this.getNodeName(featureCollectionEle); // this returns lowercase by default
  137. const isFeatureCollection = featureCollectionEle && featureCollectionEle.nodeName && nodeName.includes("featurecollection");
  138.  
  139. // Validate the document structure
  140. if (!isFeatureCollection) {
  141. console.error("Invalid GML structure: The document does not contain a valid FeatureCollection element.");
  142. return geojson; // Return empty GeoJSON if the structure is incorrect
  143. }
  144.  
  145. const features = [];
  146.  
  147. // Iterate over each child node to extract feature members
  148. for (let i = 0; i < featureCollectionEle.children.length; i++) {
  149. const featureEle = featureCollectionEle.children.item(i);
  150.  
  151. if (featureEle) {
  152. const childNodeName = this.getNodeName(featureEle);
  153.  
  154. // Identify and collect feature member elements
  155. if (childNodeName.includes("featuremember") && featureEle.children[0]) {
  156. features.push(featureEle.children[0]);
  157. }
  158. }
  159. }
  160.  
  161. // Process each feature member to extract properties and geometry
  162. for (let i = 0, len = features.length; i < len; i++) {
  163. const f = features[i];
  164.  
  165. const properties = this.getFeatureEleProperties(f); // Extract properties
  166. const geometry = this.getFeatureEleGeometry(f, crsName); // Extract geometry using the provided CRS
  167.  
  168. if (!geometry || !properties) {
  169. console.error(`Skipping feature ${i + 1} due to missing geometry or properties.`);
  170. continue; // Skip if geometry or properties are missing
  171. }
  172.  
  173. const feature = {
  174. type: "Feature",
  175. geometry,
  176. properties,
  177. };
  178. geojson.features.push(feature); // Add the feature to the GeoJSON features array
  179. }
  180.  
  181. return geojson; // Return the constructed GeoJSON object
  182. };
  183.  
  184. /**
  185. * Retrieves the CRS (Coordinate Reference System) from GML.
  186. * @param {string} gmlString - The GML string.
  187. * @returns {string|null} - The CRS name or null.
  188. */
  189. // Enhanced getCRS function to search for srsName attribute in various geometry nodes
  190. GeoGMLer.prototype.getCRS = function (xmlDoc) {
  191. // Define a list of common GML geometry elements to check for srsName attribute
  192. const geometryTags = [
  193. "gml:Envelope",
  194. "gml:Point",
  195. "gml:LineString",
  196. "gml:Polygon",
  197. "gml:MultiPoint",
  198. "gml:MultiLineString",
  199. "gml:MultiPolygon",
  200. "gml:Surface",
  201. "gml:Solid",
  202. // Add other geometry types as needed
  203. ];
  204.  
  205. for (const tag of geometryTags) {
  206. const elements = xmlDoc.getElementsByTagName(tag);
  207. for (let i = 0; i < elements.length; i++) {
  208. const srsName = elements[i].getAttribute("srsName");
  209. if (srsName) {
  210. return srsName.trim();
  211. }
  212. }
  213. }
  214.  
  215. // Consider additional handling or logging if no srsName is found
  216. return null;
  217. };
  218.  
  219. /**
  220. * Extracts the geometry from a GML feature element.
  221. * @param {Element} featureEle - The feature element.
  222. * @param {string} crsName - The name of the CRS.
  223. * @returns {Object|null} - The geometry object or null.
  224. */
  225. GeoGMLer.prototype.getFeatureEleGeometry = function (featureEle, crsName) {
  226. const children = featureEle.children || [];
  227. let type;
  228. let coordinates = [];
  229.  
  230. for (let i = 0, len = children.length; i < len; i++) {
  231. const node = children[i];
  232. const nodeName = this.getNodeName(node);
  233. if (!this.isGeoAttribute(nodeName)) {
  234. continue;
  235. }
  236.  
  237. if (node.children && node.children[0]) {
  238. type = node.children[0].nodeName.split("gml:")[1] || "";
  239. if (!type) {
  240. continue;
  241. }
  242. const geoElement = node.children[0];
  243.  
  244. if (type === "Point") {
  245. coordinates = this.processPoint(geoElement, crsName);
  246. break;
  247. } else if (type === "MultiPoint") {
  248. coordinates = this.processMultiPoint(geoElement, crsName);
  249. break;
  250. } else if (type === "MultiSurface" || type === "MultiPolygon") {
  251. coordinates = this.processMultiSurface(geoElement, crsName);
  252. break;
  253. } else if (type === "MultiCurve" || type === "MultiLineString") {
  254. coordinates = this.processMultiCurve(geoElement, crsName);
  255. break;
  256. } else if (geoElement.children.length > 0) {
  257. let geoNodes = Array.from(geoElement.children);
  258. if (this.isMulti(this.getNodeName(geoNodes[0]))) {
  259. geoNodes = this.flatMultiGeoNodes(geoNodes);
  260. }
  261.  
  262. if (geoNodes.length) {
  263. geoNodes.forEach((geoNode) => {
  264. let coords = this.parseGeoCoordinates(geoNode.children, crsName);
  265. if (!this.geoIsPolygon(type) && this.isMultiLine(type)) {
  266. coords = coords[0];
  267. }
  268. coordinates.push(coords);
  269. });
  270. break;
  271. }
  272. }
  273. }
  274. }
  275.  
  276. if (!type || !coordinates.length) {
  277. return null;
  278. }
  279.  
  280. return {
  281. type: this.mapGmlTypeToGeoJson(type),
  282. coordinates,
  283. };
  284. };
  285.  
  286. /**
  287. * Processes a multi-surface element to extract polygons.
  288. * @param {Element} multiSurfaceElement - The multi-surface element.
  289. * @param {string} crsName - The name of the CRS.
  290. * @returns {Array} - Array of polygons.
  291. */
  292. GeoGMLer.prototype.processMultiSurface = function (multiSurfaceElement, crsName) {
  293. const polygons = [];
  294. const surfaceMembers = multiSurfaceElement.getElementsByTagName("gml:surfaceMember");
  295.  
  296. for (let j = 0; j < surfaceMembers.length; j++) {
  297. const polygon = this.processPolygon(surfaceMembers[j].getElementsByTagName("gml:Polygon")[0], crsName);
  298. if (polygon) {
  299. polygons.push(polygon);
  300. }
  301. }
  302. return polygons;
  303. };
  304.  
  305. /**
  306. * Processes a polygon element.
  307. * @param {Element} polygonElement - The polygon element.
  308. * @param {string} crsName - The name of the CRS.
  309. * @returns {Array} - Array representing the polygon.
  310. */
  311. GeoGMLer.prototype.processPolygon = function (polygonElement, crsName) {
  312. const polygon = [];
  313. const exteriorElements = polygonElement.getElementsByTagName("gml:exterior");
  314.  
  315. if (exteriorElements.length > 0) {
  316. const exterior = this.parseRing(exteriorElements[0], crsName);
  317. if (exterior) {
  318. polygon.push(exterior);
  319. }
  320. }
  321.  
  322. const interiorElements = polygonElement.getElementsByTagName("gml:interior");
  323. for (let k = 0; k < interiorElements.length; k++) {
  324. const interior = this.parseRing(interiorElements[k], crsName);
  325. if (interior) {
  326. polygon.push(interior);
  327. }
  328. }
  329. return polygon;
  330. };
  331.  
  332. /**
  333. * Parses a ring element to extract coordinates.
  334. * @param {Element} ringElement - The ring element.
  335. * @param {string} crsName - The name of the CRS.
  336. * @returns {Array} - Array of coordinates.
  337. */
  338. GeoGMLer.prototype.parseRing = function (ringElement, crsName) {
  339. const coordNodes = ringElement.getElementsByTagName("gml:posList");
  340. if (coordNodes.length > 0) {
  341. return this.parseGeoCoordinates(coordNodes, crsName);
  342. }
  343. return [];
  344. };
  345.  
  346. /**
  347. * Processes a multi-curve element to extract line strings.
  348. * @param {Element} multiCurveElement - The multi-curve element.
  349. * @param {string} crsName - The name of the CRS.
  350. * @returns {Array} - Array of line strings.
  351. */
  352. GeoGMLer.prototype.processMultiCurve = function (multiCurveElement, crsName) {
  353. const lineStrings = [];
  354. const curveMembers = multiCurveElement.getElementsByTagName("gml:curveMember");
  355.  
  356. for (let j = 0; j < curveMembers.length; j++) {
  357. const lineStringElement = curveMembers[j].getElementsByTagName("gml:LineString")[0];
  358. if (lineStringElement) {
  359. const lineString = this.processLineString(lineStringElement, crsName);
  360. if (lineString) {
  361. lineStrings.push(lineString);
  362. }
  363. }
  364. }
  365. return lineStrings;
  366. };
  367.  
  368. /**
  369. * Processes a line string element.
  370. * @param {Element} lineStringElement - The line string element.
  371. * @param {string} crsName - The name of the CRS.
  372. * @returns {Array} - Array of coordinates representing the line string.
  373. */
  374. GeoGMLer.prototype.processLineString = function (lineStringElement, crsName) {
  375. const coordNodes = lineStringElement.getElementsByTagName("gml:posList");
  376. if (coordNodes.length > 0) {
  377. return this.parseGeoCoordinates(coordNodes, crsName);
  378. }
  379. return [];
  380. };
  381.  
  382. /**
  383. * Processes a GML Point geometry element to extract its coordinates.
  384. *
  385. * @param {Element} geoElement - The GML element representing the Point geometry.
  386. * @param {string} crsName - The coordinate reference system (CRS) name, used to determine if coordinate order needs to be reversed.
  387. *
  388. * @returns {Array} An array containing the coordinates for the Point. If no valid coordinates are found, an empty array is returned.
  389. *
  390. * The function first attempts to find the coordinates using the `<gml:pos>` element. If that is not available,
  391. * it looks for the `<gml:coordinates>` element instead. It utilizes the `parseGeoCoordinates` method to convert
  392. * the raw coordinate text into an array of numbers, considering the specified CRS.
  393. */
  394. GeoGMLer.prototype.processPoint = function (geoElement, crsName) {
  395. let coordNode = geoElement.getElementsByTagName("gml:pos");
  396.  
  397. if (coordNode.length === 0) {
  398. coordNode = geoElement.getElementsByTagName("gml:coordinates");
  399. }
  400.  
  401. if (coordNode.length > 0) {
  402. // Parse the coordinates
  403. const parsedCoords = this.parseGeoCoordinates([coordNode[0]], crsName);
  404. // Flatten them if necessary (should only be length 1 for a valid Point)
  405. return parsedCoords.length > 0 ? parsedCoords[0] : [];
  406. } else {
  407. return [];
  408. }
  409. };
  410.  
  411. /**
  412. * Processes a multi-point element to extract the coordinates of each point.
  413. * @param {Element} multiPointElement - The element representing the MultiPoint geometry.
  414. * @param {string} crsName - The coordinate reference system (CRS) name.
  415. * @returns {Array} - An array of coordinate arrays for the multipoint.
  416. */
  417. GeoGMLer.prototype.processMultiPoint = function (multiPointElement, crsName) {
  418. const points = [];
  419. const pointMembers = multiPointElement.getElementsByTagName("gml:pointMember");
  420.  
  421. for (let j = 0; j < pointMembers.length; j++) {
  422. const pointElement = pointMembers[j].getElementsByTagName("gml:Point")[0];
  423. if (pointElement) {
  424. const coordinates = this.processPoint(pointElement, crsName);
  425. if (coordinates.length > 0) {
  426. points.push(coordinates);
  427. }
  428. }
  429. }
  430.  
  431. return points;
  432. };
  433.  
  434. /**
  435. * Parses coordinate nodes into arrays of coordinates, considering the coordinate reference system (CRS)
  436. * and the coordinate formatting requirements.
  437. *
  438. * @param {HTMLCollection} coordNodes - The collection of coordinate nodes to be parsed.
  439. * @param {string} crsName - The name of the coordinate reference system (CRS).
  440. * @returns {Array} - An array of parsed coordinates.
  441. *
  442. * The `needsReversal` flag is determined by the CRS name. It is set to true for common geographic
  443. * coordinate systems like "EPSG:4326", "CRS84", or "WGS84", which typically use a "latitude, longitude"
  444. * format. Reversing is necessary when converting to systems expecting the "longitude, latitude" order
  445. * like geoJSON.
  446. *
  447. * The `isCommaSeparated` flag is used to determine the delimiter in the coordinate parsing. It checks
  448. * if the coordinates node is named with ":coordinates", which indicates that commas are used to
  449. * separate coordinate values (older versions of GML). This is essential for correctly interpreting data where commas are
  450. * the delimiter, distinguishing from systems using whitespace (GML3.X) with :pos and :posList.
  451. */
  452. GeoGMLer.prototype.parseGeoCoordinates = function (coordNodes, crsName) {
  453. const coordinates = [];
  454. const needsReversal = crsName.includes("4326") || crsName.includes("CRS84") || crsName.includes("WGS84");
  455.  
  456. if (coordNodes.length === 0) {
  457. }
  458.  
  459. for (let i = 0, len = coordNodes.length; i < len; i++) {
  460. const coordNode = this.findCoordsNode(coordNodes[i]);
  461.  
  462. if (!coordNode) {
  463. continue;
  464. }
  465.  
  466. const isCommaSeparated = this.getNodeName(coordNode).indexOf(":coordinates") > -1;
  467. const textContent = coordNode.textContent.trim();
  468. const coords = this.parseCoordinates(textContent, isCommaSeparated, needsReversal);
  469.  
  470. coordinates.push(...coords);
  471. }
  472. return coordinates;
  473. };
  474.  
  475. /**
  476. * Parses a coordinate string into an array of coordinate pairs, considering whether the input
  477. * uses commas as separators and whether the coordinate pair order needs to be reversed.
  478. *
  479. * @param {string} text - The text containing coordinates, which may be in different formats
  480. * based on the input data (e.g., comma-separated or space-separated).
  481. * @param {boolean} isCommaSeparated - A flag indicating if the coordinate string uses commas as
  482. * separators between individual coordinates. This is often observed in older data formats where
  483. * coordinates are presented as "x,y".
  484. * @param {boolean} needsReversal - A flag indicating if the latitude and longitude values need to
  485. * be reversed in order, which is particularly necessary for compatibility with modern formats like
  486. * GeoJSON. Older versions of GML (such as 1 and 2), when using the :coordinates tag and a CRS like
  487. * "EPSG:4326", often present coordinates in "latitude, longitude" format. In contrast, GeoJSON and
  488. * other modern systems require "longitude, latitude". However, in GML 3.x, it is more common to use
  489. * elements like :pos and :posList, which typically follow the "longitude, latitude" order, aligning
  490. * with modern geographic data representations regardless of the projection system used.
  491. * @returns {Array} - An array of coordinate pairs, where each pair is represented as an array of the
  492. * form [longitude, latitude] or [latitude, longitude] depending on the `needsReversal` flag.
  493. */
  494. GeoGMLer.prototype.parseCoordinates = function (text, isCommaSeparated, needsReversal) {
  495. if (!text) return [];
  496.  
  497. const coords = text.trim().split(/\s+/);
  498. const coordinates = [];
  499.  
  500. for (let i = 0; i < coords.length; i++) {
  501. let c1, c2;
  502. const coord = coords[i];
  503.  
  504. if (isCommaSeparated) {
  505. if (coord.includes(",")) {
  506. const [x, y] = coord.split(",");
  507. c1 = this.trimAndParse(x);
  508. c2 = this.trimAndParse(y);
  509. coordinates.push(needsReversal ? [c1, c2] : [c2, c1]);
  510. }
  511. } else {
  512. c1 = this.trimAndParse(coord);
  513. c2 = this.trimAndParse(coords[i + 1]);
  514. i++; // Skip the next coordinate since it's already processed
  515. coordinates.push(needsReversal ? [c2, c1] : [c1, c2]);
  516. }
  517. }
  518. return coordinates;
  519. };
  520.  
  521. /**
  522. * Trims and parses a string into a float.
  523. * @param {string} str - The string to parse.
  524. * @returns {number} - The parsed float.
  525. */
  526. GeoGMLer.prototype.trimAndParse = function (str) {
  527. return parseFloat(str.replace(/\s+/g, ""));
  528. };
  529.  
  530. /**
  531. * Finds the coordinate node within a given node.
  532. * @param {Node} node - The node to search.
  533. * @returns {Node} - The coordinate node found.
  534. */
  535. GeoGMLer.prototype.findCoordsNode = function (node) {
  536. let nodeName = this.getNodeName(node);
  537.  
  538. while (nodeName.indexOf(":coordinates") === -1 && nodeName.indexOf(":posList") === -1 && nodeName.indexOf(":pos") === -1) {
  539. node = node.children[0];
  540. nodeName = this.getNodeName(node);
  541. }
  542. return node;
  543. };
  544.  
  545. /**
  546. * Retrieves the node name.
  547. * @param {Node} node - The node object.
  548. * @param {boolean} [lowerCase=true] - Whether to convert the name to lower case.
  549. * @returns {string} - The node name.
  550. */
  551. GeoGMLer.prototype.getNodeName = function (node, lowerCase = true) {
  552. if (lowerCase) {
  553. return (node.nodeName || "").toLocaleLowerCase();
  554. } else {
  555. return node.nodeName || "";
  556. }
  557. };
  558.  
  559. /**
  560. * Checks if the geometry type is a polygon.
  561. * @param {string} type - The geometry type.
  562. * @returns {boolean} - True if the type is a polygon.
  563. */
  564. GeoGMLer.prototype.geoIsPolygon = function (type) {
  565. return type.indexOf("Polygon") > -1;
  566. };
  567.  
  568. /**
  569. * Maps GML geometry types to GeoJSON types.
  570. * @param {string} type - The GML type.
  571. * @returns {string} - The corresponding GeoJSON type.
  572. */
  573. GeoGMLer.prototype.mapGmlTypeToGeoJson = function (type) {
  574. switch (type) {
  575. case "MultiCurve":
  576. return "MultiLineString";
  577. case "MultiSurface":
  578. return "MultiPolygon";
  579. default:
  580. return type; // Return as-is for matching types
  581. }
  582. };
  583.  
  584. /**
  585. * Extracts feature element properties.
  586. * @param {Element} featureEle - The feature element.
  587. * @returns {Object} - The properties object.
  588. */
  589. GeoGMLer.prototype.getFeatureEleProperties = function (featureEle) {
  590. const children = featureEle.children || [];
  591. const properties = {};
  592.  
  593. for (let i = 0, len = children.length; i < len; i++) {
  594. const node = children[i];
  595. const nodeName = this.getNodeName(node);
  596.  
  597. // Skip geometry-related attributes
  598. if (this.isGeoAttribute(nodeName) && node.children.length) {
  599. continue;
  600. }
  601.  
  602. // Skip boundedBy or other GML-specific elements
  603. if (nodeName === "gml:boundedby" || nodeName === "gml:geometryproperty") {
  604. continue;
  605. }
  606.  
  607. // Extract feature properties
  608. const key = node.nodeName.includes(":") ? node.nodeName.split(":")[1] : node.nodeName;
  609. if (!key) {
  610. continue;
  611. }
  612.  
  613. const value = node.textContent || "";
  614. properties[key] = value;
  615. }
  616. return properties;
  617. };
  618.  
  619. /**
  620. * Flattens multi-geometry nodes.
  621. * @param {Array} nodes - The multi-geometry nodes.
  622. * @returns {Array} - Array of geometry nodes.
  623. */
  624. GeoGMLer.prototype.flatMultiGeoNodes = function (nodes) {
  625. const geoNodes = [];
  626. for (let i = 0, len = nodes.length; i < len; i++) {
  627. const children = nodes[i].children;
  628. for (let j = 0, len1 = children.length; j < len1; j++) {
  629. geoNodes.push(children[j].children[0]);
  630. }
  631. }
  632. return geoNodes;
  633. };
  634.  
  635. /**
  636. * Checks if the node name indicates a multi-geometry.
  637. * @param {string} nodeName - The node name.
  638. * @returns {boolean} - True if the node denotes a multi-geometry.
  639. */
  640. GeoGMLer.prototype.isMulti = function (nodeName) {
  641. return nodeName.indexOf("member") > -1;
  642. };
  643.  
  644. /**
  645. * Checks if the geometry type is a multi-line.
  646. * @param {string} type - The geometry type.
  647. * @returns {boolean} - True if the type is a multi-line.
  648. */
  649. GeoGMLer.prototype.isMultiLine = function (type) {
  650. return type === "MultiCurve" || type === "MultiLineString";
  651. };
  652.  
  653. /**
  654. * Checks if the node name is a geometry attribute.
  655. * @param {string} nodeName - The node name.
  656. * @returns {boolean} - True if the attribute is geometry-related.
  657. */
  658. GeoGMLer.prototype.isGeoAttribute = function (nodeName) {
  659. return GEONODENAMES.some((geoName) => nodeName.indexOf(geoName) > -1);
  660. };
  661.  
  662. return GeoGMLer;
  663. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址