Search code examples
angulartypescriptgeojson

can't access 'coordinates' member of geojson feature collection


I have a GeoJSON feature collection in my Angular app that is an array of features, each containing a geometry object and properties object. Structure looks like this:

import { FeatureCollection, Feature } from 'geojson';

staticBreadcrumbs: GeoJSON.FeatureCollection<GeoJSON.Geometry>;

this.staticBreadcrumbs = {
    type : 'FeatureCollection',
    features: [
    {
        type: 'Feature',
        properties: {
            property1:  'property1',
            property2:  'property2'
        },
        geometry: {
            type: 'Point',
            coordinates: [-117.14024305343628, 32.81294345855713]
        }
    },
    {
        type: 'Feature',
        properties: {
        ...

I'm trying to create a mapboxgl marker for each feature in the collection and need to get the coordinates from each GeoJSON object, the compiler is telling me that the coordinates aren't part of the feature. The specific error is: Property 'coordinates' does not exist on type 'Geometry'. Property 'coordinates' does not exist on type 'GeometryCollection'.

console.log('this.staticBreadcrumbs.features[0]', this.staticBreadcrumbs.features[0]);

var marker = new Marker(el)
    .setLngLat([
        this.staticBreadcrumbs.features[0].geometry.coordinates[0],
        this.staticBreadcrumbs.features[0].geometry.coordinates[1]
    ])
    .addTo(this.map);

My console.log displays

 this.staticBreadcrumbs.features[0]
 {type: "Feature", properties: {…}, geometry: {…}}
 1.  geometry:
     1.  coordinates: Array(2)
         1.  0: -117.14024305343628
         2.  1: 32.81294345855713
         3.  length: 2
         4.  __proto__: Array(0)
     2.  type: "Point"
   2. __proto__: Object
2. properties: {deviceID: "CAP498", altitude: 401.6721913312, autonomous: 0, azimuth: 0, batteryStatusLabel: "Nominal", …}
3. type: "Feature"
4. __proto__: Object

The coordinates are exactly where I expect them to be, but I can't get to them. I have tried all sort of different ways to declare the Feature collection, but I can't find the right combination.

What do I need to do in order to access the coordinates?

Thanks.....


Solution

  • What do I need to do in order to access the coordinates?

    Short Answer

    Check that geometry is a Point before trying to access its coordinates property.

    if (staticBreadcrumbs.features[0].geometry.type === 'Point') {
    
        var marker = new Marker(el)
            .setLngLat([
                this.staticBreadcrumbs.features[0].geometry.coordinates[0],
                this.staticBreadcrumbs.features[0].geometry.coordinates[1]
            ])
            .addTo(this.map);
    
    }
    

    Explanation

    The problem you're experiencing happens because Geometry is a union type.

    A union type describes a value that can be one of several types. We use the vertical bar (|) to separate each type...

    This is the Geometry union type definition:

    export type Geometry = 
        Point |
        MultiPoint | 
        LineString | 
        MultiLineString | 
        Polygon | 
        MultiPolygon | 
        GeometryCollection;
    

    As you can see, the Geometry type is a union of seven types. Unfortunately, not all of those types include the coordinates property.

    If we have a value that has a union type, we can only access members that are common to all types in the union.

    That is why we are only able to access the coordinates property after we have narrowed the type.

    Some Other Options

    If you're using only Point types in your FeatureCollection, then use the Point type as the generic parameter:

    let staticBreadcrumbs: FeatureCollection<Point>;
    

    If you're using a variety of types in your FeatureCollection, then use a cast to tell the type checker that you're sure you have a Point:

    (staticBreadcrumbs.features[0].geometry as Point).coordinates[0]
    

    As shown in the Short Answer, instead of using a cast, you can use a conditional statement to narrow the type:

    const geometry = staticBreadcrumbs.features[0].geometry;
    
    if (geometry.type === 'Point') {
        const coordinates00 = geometry.coordinates[0];
        const coordinates01 = geometry.coordinates[1];
    }
    

    There is a short demo of that last example here.