-
Notifications
You must be signed in to change notification settings - Fork 68
Description
Hi Tom,
over years I come back to ask myself this question: Is it valid to have a properties element in an geoJSON featureCollection?
So first of all here's just a friendly reminder:
• RFC 7946 - Extending GeoJSON
6.1. Foreign Members
Members not described in this specification ("foreign members") MAY be used in a GeoJSON document. Note that support for foreign members can vary across implementations, and no normative processing model for foreign members is defined. Accordingly, implementations that rely too heavily on the use of foreign members might experience reduced interoperability with other implementations.
For example, in the (abridged) Feature object shown below
the name/value pair of "title": "Example Feature" is a foreign member. When the value of a foreign member is an object, all the descendant members of that object are themselves foreign members.
• GPX 1.1 Schema Documentation - metadataType
<xsd:complexType name="metadataType">
<xsd:sequence>
<-- elements must appear in this order -->
<xsd:element name="name" type="[xsd](https://www.topografix.com/GPX/1/1/#ns_xsd):string" minOccurs="0"/>
<xsd:element name="desc" type="[xsd](https://www.topografix.com/GPX/1/1/#ns_xsd):string" minOccurs="0"/>
<xsd:element name="author" type="[personType](https://www.topografix.com/GPX/1/1/#type_personType)" minOccurs="0"/>
<xsd:element name="copyright" type="[copyrightType](https://www.topografix.com/GPX/1/1/#type_copyrightType)" minOccurs="0"/>
<xsd:element name="link" type="[linkType](https://www.topografix.com/GPX/1/1/#type_linkType)" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="time" type="[xsd](https://www.topografix.com/GPX/1/1/#ns_xsd):dateTime" minOccurs="0"/>
<xsd:element name="keywords" type="[xsd](https://www.topografix.com/GPX/1/1/#ns_xsd):string" minOccurs="0"/>
<xsd:element name="bounds" type="[boundsType](https://www.topografix.com/GPX/1/1/#type_boundsType)" minOccurs="0"/>
<xsd:element name="extensions" type="[extensionsType](https://www.topografix.com/GPX/1/1/#type_extensionsType)" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>Motivation
Mainly, make it easier to access the root gpx file <name>.
Right now, others can achieve this more or less by doing like so:
// Ref: https://github.com/Raruto/leaflet-elevation/blob/e0c68cba9a71d140e4c5a4179c51ae34588b7327/src/control.js#L965-L973
let xml = (new DOMParser()).parseFromString(data, "text/xml");
let type = xml.documentElement.tagName.toLowerCase(); // "kml" or "gpx"
let name = xml.getElementsByTagName('name');
if (xml.getElementsByTagName('parsererror').length) {
throw 'Invalid XML';
}
if (!(type in toGeoJSON)) {
type = xml.documentElement.tagName == "TrainingCenterDatabase" ? 'tcx' : 'gpx';
}
let geojson = toGeoJSON[type](xml);
geojson.name = name.length > 0 ? (Array.from(name).find(tag => tag.parentElement.tagName == "trk") ?? name[0]).textContent : '';Draft implementation
Essentially, augmenting the FeatureCollection interface by providing a new property: metadata
• lib/gpx.ts#L181-L197
import { extractMetadata } from "./gpx/metadata";
...
export function gpx(node: Document): FeatureCollection {
return {
type: "FeatureCollection",
features: Array.from(gpxGen(node)),
metadata: extractMetadata(node),
};
}• lib/gpx/metadata.ts
Almost the same as: lib/gpx/properties.ts#L1-L36
NB some functions parameters types invoked in here must be double-checked (ref: Element and Document interfaces)
import { $, getMulti, nodeVal } from "../shared";
export function extractMetadata(node: Document) {
const properties = getMulti(node, [
"name",
"desc",
// "author",
// "copyright",
"time",
"keywords",
// bounds
]);
const extensions = Array.from(
node.getElementsByTagNameNS(
"http://www.garmin.com/xmlschemas/GpxExtensions/v3",
"*"
)
);
for (const child of extensions) {
if (child.parentNode?.parentNode === node) {
properties[child.tagName.replace(":", "_")] = nodeVal(child);
}
}
const links = $(node, "link");
if (links.length) {
properties.links = links.map((link) =>
Object.assign(
{ href: link.getAttribute("href") },
getMulti(link, ["text", "type"])
)
);
}
return properties;
}lib/index.d.ts
I'm not very knowledgeable about typescript, anyway I think it could result in something like this:
// Based on https://stackoverflow.com/questions/42262565/how-to-augment-typescript-interface-in-d-ts
import * as geojson from 'geojson';
declare module 'geojson' {
namespace GeoJSON {
export interface FeatureCollection<G extends geojson.Geometry | null = geojson.Geometry, P = geojson.GeoJsonProperties> extends geojson.GeoJsonObject {
metadata?: {
name?: string;
desc?: string;
author?: {
name?: string;
email?: string;
link?: {
href: string;
text?: string;
type?: string;
};
};
copyright?: {
author?: string;
year?: string;
license?: string;
};
time?: string;
keywords?: string;
bounds?: [number, number, number, number]
extensions?: any;
};
}
}
}I'm really sorry, but at the moment I can't say how much additional re-work might be needed to integrate it in the generation of current dist/index.d.ts file.
Hoping that somehow these notes can be useful
👋 Raruto
{ "type": "Feature", "id": "f1", "geometry": {...}, "properties": {...}, "title": "Example Feature" }