diff --git a/lib/__snapshots__/index.test.ts.snap b/lib/__snapshots__/index.test.ts.snap index 325e2be..50f22c0 100644 --- a/lib/__snapshots__/index.test.ts.snap +++ b/lib/__snapshots__/index.test.ts.snap @@ -21730,6 +21730,122 @@ Object { } `; +exports[`toGeoJSON schema.kml 1`] = ` +Object { + "features": Array [ + Object { + "geometry": Object { + "coordinates": Array [ + Array [ + Array [ + 12.8206124, + 55.9654246, + ], + Array [ + 12.8204862, + 55.9654342, + ], + Array [ + 12.8204902, + 55.9654507, + ], + Array [ + 12.8206164, + 55.9654411, + ], + Array [ + 12.8206124, + 55.9654246, + ], + ], + ], + "type": "Polygon", + }, + "properties": Object { + "block_block_number": 1, + "cn_cv_1109": "89%", + "fill-opacity": 0, + "gid": 167001, + "plotno_plot_number": 167001, + "random": 0.9, + "random_2": 0.4, + "random_2_block": 41, + "random_2_trial": 39, + "random_block": 98, + "random_trial": 96, + "stroke": "#ff0000", + "stroke-opacity": 1, + "tream_treatment_number": "16", + }, + "type": "Feature", + }, + ], + "type": "FeatureCollection", +} +`; + +exports[`toGeoJSON schema.kml 2`] = ` +Object { + "children": Array [ + Object { + "children": Array [ + Object { + "geometry": Object { + "coordinates": Array [ + Array [ + Array [ + 12.8206124, + 55.9654246, + ], + Array [ + 12.8204862, + 55.9654342, + ], + Array [ + 12.8204902, + 55.9654507, + ], + Array [ + 12.8206164, + 55.9654411, + ], + Array [ + 12.8206124, + 55.9654246, + ], + ], + ], + "type": "Polygon", + }, + "properties": Object { + "block_block_number": 1, + "cn_cv_1109": "89%", + "fill-opacity": 0, + "gid": 167001, + "plotno_plot_number": 167001, + "random": 0.9, + "random_2": 0.4, + "random_2_block": 41, + "random_2_trial": 39, + "random_block": 98, + "random_trial": 96, + "stroke": "#ff0000", + "stroke-opacity": 1, + "tream_treatment_number": "16", + }, + "type": "Feature", + }, + ], + "meta": Object { + "name": "TES_124_167_28_JUL_2022_1109", + }, + "type": "folder", + }, + ], + "type": "root", +} +`; + exports[`toGeoJSON selfclosing.kml 1`] = ` Object { "features": Array [ @@ -21772,9 +21888,9 @@ Object { "type": "Point", }, "properties": Object { - "ElevationGain": "10", + "ElevationGain": 10, "TrailHeadName": "Pi in the sky", - "TrailLength": "3.14159", + "TrailLength": 3.14159, "name": "Easy trail", "styleUrl": "#trailhead-balloon-template", }, @@ -21789,9 +21905,9 @@ Object { "type": "Point", }, "properties": Object { - "ElevationGain": "10000", + "ElevationGain": 10000, "TrailHeadName": "Mount Everest", - "TrailLength": "347.45", + "TrailLength": 347.45, "name": "Difficult trail", "styleUrl": "#trailhead-balloon-template", }, @@ -21814,9 +21930,9 @@ Object { "type": "Point", }, "properties": Object { - "ElevationGain": "10", + "ElevationGain": 10, "TrailHeadName": "Pi in the sky", - "TrailLength": "3.14159", + "TrailLength": 3.14159, "name": "Easy trail", "styleUrl": "#trailhead-balloon-template", }, @@ -21831,9 +21947,9 @@ Object { "type": "Point", }, "properties": Object { - "ElevationGain": "10000", + "ElevationGain": 10000, "TrailHeadName": "Mount Everest", - "TrailLength": "347.45", + "TrailLength": 347.45, "name": "Difficult trail", "styleUrl": "#trailhead-balloon-template", }, diff --git a/lib/kml.ts b/lib/kml.ts index 1edd83e..80fc580 100644 --- a/lib/kml.ts +++ b/lib/kml.ts @@ -12,6 +12,7 @@ import { isElement, normalizeId, } from "./shared"; +import { Schema, typeConverters } from "./kml/shared"; /** * A folder including metadata. Folders @@ -78,6 +79,16 @@ function buildStyleMap(node: Document): StyleMap { return styleMap; } +function buildSchema(node: Document): Schema { + const schema: Schema = {}; + for (const field of $(node, "SimpleField")) { + schema[field.getAttribute("name") || ""] = + typeConverters[field.getAttribute("type") || ""] || + typeConverters["string"]; + } + return schema; +} + const FOLDER_PROPS = [ "name", "visibility", @@ -146,6 +157,7 @@ function getFolder(node: Element): Folder { */ export function kmlWithFolders(node: Document): Root { const styleMap = buildStyleMap(node); + const schema = buildSchema(node); // atomic geospatial types supported by KML - MultiGeometry is // handled separately @@ -161,7 +173,7 @@ export function kmlWithFolders(node: Document): Root { switch (node.tagName) { case "GroundOverlay": { placemarks.push(node); - const placemark = getGroundOverlay(node, styleMap); + const placemark = getGroundOverlay(node, styleMap, schema); if (placemark) { pointer.children.push(placemark); } @@ -169,7 +181,7 @@ export function kmlWithFolders(node: Document): Root { } case "Placemark": { placemarks.push(node); - const placemark = getPlacemark(node, styleMap); + const placemark = getPlacemark(node, styleMap, schema); if (placemark) { pointer.children.push(placemark); } @@ -203,12 +215,13 @@ export function kmlWithFolders(node: Document): Root { */ export function* kmlGen(node: Document): Generator { const styleMap = buildStyleMap(node); + const schema = buildSchema(node); for (const placemark of $(node, "Placemark")) { - const feature = getPlacemark(placemark, styleMap); + const feature = getPlacemark(placemark, styleMap, schema); if (feature) yield feature; } for (const groundOverlay of $(node, "GroundOverlay")) { - const feature = getGroundOverlay(groundOverlay, styleMap); + const feature = getGroundOverlay(groundOverlay, styleMap, schema); if (feature) yield feature; } } diff --git a/lib/kml/ground_overlay.ts b/lib/kml/ground_overlay.ts index ac28623..7c3c59f 100644 --- a/lib/kml/ground_overlay.ts +++ b/lib/kml/ground_overlay.ts @@ -6,6 +6,7 @@ import { extractTimeSpan, extractTimeStamp, getMaybeHTMLDescription, + Schema, } from "./shared"; import { extractIconHref, extractStyle } from "./extractStyle"; import { coord, fixRing, getCoordinates } from "./geometry"; @@ -91,7 +92,8 @@ function getLatLonBox(node: Element): Polygon | null { export function getGroundOverlay( node: Element, - styleMap: StyleMap + styleMap: StyleMap, + schema: Schema ): Feature { const geometry = getGroundOverlayBox(node); @@ -116,7 +118,7 @@ export function getGroundOverlay( extractCascadedStyle(node, styleMap), extractStyle(node), extractIconHref(node), - extractExtendedData(node), + extractExtendedData(node, schema), extractTimeSpan(node), extractTimeStamp(node) ), diff --git a/lib/kml/placemark.ts b/lib/kml/placemark.ts index 899a024..554924b 100644 --- a/lib/kml/placemark.ts +++ b/lib/kml/placemark.ts @@ -6,6 +6,7 @@ import { extractTimeSpan, extractTimeStamp, getMaybeHTMLDescription, + Schema, } from "./shared"; import { extractStyle } from "./extractStyle"; import { getGeometry } from "./geometry"; @@ -23,7 +24,8 @@ function geometryListToGeometry(geometries: Geometry[]): Geometry | null { export function getPlacemark( node: Element, - styleMap: StyleMap + styleMap: StyleMap, + schema: Schema ): Feature { const { coordTimes, geometries } = getGeometry(node); @@ -42,7 +44,7 @@ export function getPlacemark( getMaybeHTMLDescription(node), extractCascadedStyle(node, styleMap), extractStyle(node), - extractExtendedData(node), + extractExtendedData(node, schema), extractTimeSpan(node), extractTimeStamp(node), coordTimes.length diff --git a/lib/kml/shared.ts b/lib/kml/shared.ts index 63b8ace..ac882af 100644 --- a/lib/kml/shared.ts +++ b/lib/kml/shared.ts @@ -9,7 +9,22 @@ import { val1, } from "../shared"; -export function extractExtendedData(node: Element) { +export type TypeConverter = (x: string) => unknown; +export type Schema = { [key: string]: TypeConverter }; + +const toNumber: TypeConverter = (x) => Number(x); +export const typeConverters: Record = { + string: (x) => x, + int: toNumber, + uint: toNumber, + short: toNumber, + ushort: toNumber, + float: toNumber, + double: toNumber, + bool: (x) => Boolean(x), +}; + +export function extractExtendedData(node: Element, schema: Schema) { return get(node, "ExtendedData", (extendedData, properties) => { for (const data of $(extendedData, "Data")) { properties[data.getAttribute("name") || ""] = nodeVal( @@ -17,7 +32,9 @@ export function extractExtendedData(node: Element) { ); } for (const simpleData of $(extendedData, "SimpleData")) { - properties[simpleData.getAttribute("name") || ""] = nodeVal(simpleData); + const name = simpleData.getAttribute("name") || ""; + const typeConverter = schema[name] || typeConverters.string; + properties[name] = typeConverter(nodeVal(simpleData)); } return properties; }); diff --git a/test/data/schema.kml b/test/data/schema.kml new file mode 100644 index 0000000..25860cb --- /dev/null +++ b/test/data/schema.kml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + TES_124_167_28_JUL_2022_1109 + + + + + 167001 + 167001 + 16 + 1 + 0.9 + 0.4 + 96 + 39 + 98 + 41 + 89% + + + + + + 12.8206124,55.9654246 12.8204862,55.9654342 12.8204902,55.9654507 12.8206164,55.9654411 12.8206124,55.9654246 + + + + + + +