Skip to content
Advertisement

Group GeoJson features by property value in JavaScript

I have a list of geojson features that each have an asset ID in their properties. I want to manipulate the geojson so I am left with only a single feature per asset ID, with the properties from each feature found added to the feature properties.

As an example, the following geojson has 4 features; 2 of them have an asset ID of 100 and 2 of them have an asset ID of 200:-

{
   "type":"FeatureCollection",
   "title":"map_features",
   "features":[
      {
         "type":"Feature",
         "geometry":{
            "type":"Point",
            "coordinates":[
               525408,
               162788
            ]
         },
         "properties":{
            "CASENO":"CASE29302",
            "CASE_TYPE":"litterBin",
            "ASSETID":"100",
            "DESCRIPTION":"Bin is full"
         }
      },
      {
         "type":"Feature",
         "geometry":{
            "type":"Point",
            "coordinates":[
               525408,
               162788
            ]
         },
         "properties":{
            "CASENO":"CASE56843",
            "CASE_TYPE":"litterBin",
            "ASSETID":"100",
            "NOTES":"Bin needs emptying"
         }
      },
      {
         "type":"Feature",
         "geometry":{
            "type":"Point",
            "coordinates":[
               525177,
               164509
            ]
         },
         "properties":{
            "CASENO":"CASE77112",
            "CASE_TYPE":"litterBin",
            "ASSETID":"200",
            "NOTES":"Bin cleaned"
         }
      },
      {
         "type":"Feature",
         "geometry":{
            "type":"Point",
            "coordinates":[
               525177,
               164509
            ]
         },
         "properties":{
            "CASENO":"CASE04393",
            "CASE_TYPE":"litterBin",
            "ASSETID":"200",
            "NOTES":"Bin full"
         }
      }
   ]
}

I am looking to be left with just two features for asset ID 100 and 200, and have the properties grouped together from the duplicate features:-

{
    "type": "FeatureCollection",
    "title": "map_features",
    "features": [
        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [
                    525408,
                    162788
                ]
            },
            "properties": [
                {
                    "CASENO": "CASE29302",
                    "CASE_TYPE": "litterBin",
                    "ASSETID": "100",
                    "DESCRIPTION": "Bin is full"
                },
                {
                    "CASENO": "CASE56843",
                    "CASE_TYPE": "litterBin",
                    "ASSETID": "100",
                    "NOTES": "Bin needs emptying"
                }
            ]
        },
        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [
                    525408,
                    162788
                ]
            },
            "properties": [
                {
                    "CASENO": "CASE56843",
                    "CASE_TYPE": "litterBin",
                    "ASSETID": "100",
                    "NOTES": "Bin needs emptying"
                },
                {
                    "CASENO": "CASE04393",
                    "CASE_TYPE": "litterBin",
                    "ASSETID": "200",
                    "NOTES": "Bin full"
                }
            ]
        }
    ]
}

I am not sure if there is a javascript / jquery method that might help with this? Would I need to use a for loop?

Any pointers would be appreciated.

Advertisement

Answer

If you’re confident that the duplicate features have the same geometry you can use this approach:

  1. Group the features by ASSETID you can use a Map with reduce to do this where each key is a unique ASSETID and the value is an array that fills up with features.
  2. For each item in the Map, use the first feature in the array for the geometry and then for properties replace the array of features with an array of just the properties.
  3. Create a new featureCollection with the new features resolved in step 2.

See the comments in the working code below:

// group features by ASSETID
const assets = Array.from(
  fc.features.reduce((a, c) => {
    const id = c.properties.ASSETID;
    if (!a.has(id)) a.set(id, []); // <-- initialise each entry with an empty array
    a.get(id).push(c);             // <-- add the feature to the array for that ASSETID
    return a;
  }, new Map())                    // <-- the accumulator is a Map
);

// create new features
const newFeatures = assets.reduce((a, c) => {
  a.push({
    "type": "Feature",
    "geometry": c[1][0].geometry,  // <-- take geometry from first array entry
    "properties": c[1].map(f => f.properties) // <-- just retain the properties of the feature
  });
  return a;
}, []);

// create new feature collection
const newFc = {
   "type":"FeatureCollection",
   "title":"map_features",
   "features": newFeatures
}

// output
console.log(newFc);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script>
const fc = {
   "type":"FeatureCollection",
   "title":"map_features",
   "features":[
      {
         "type":"Feature",
         "geometry":{
            "type":"Point",
            "coordinates":[
               525408,
               162788
            ]
         },
         "properties":{
            "CASENO":"CASE29302",
            "CASE_TYPE":"litterBin",
            "ASSETID":"100",
            "DESCRIPTION":"Bin is full"
         }
      },
      {
         "type":"Feature",
         "geometry":{
            "type":"Point",
            "coordinates":[
               525408,
               162788
            ]
         },
         "properties":{
            "CASENO":"CASE56843",
            "CASE_TYPE":"litterBin",
            "ASSETID":"100",
            "NOTES":"Bin needs emptying"
         }
      },
      {
         "type":"Feature",
         "geometry":{
            "type":"Point",
            "coordinates":[
               525177,
               164509
            ]
         },
         "properties":{
            "CASENO":"CASE77112",
            "CASE_TYPE":"litterBin",
            "ASSETID":"200",
            "NOTES":"Bin cleaned"
         }
      },
      {
         "type":"Feature",
         "geometry":{
            "type":"Point",
            "coordinates":[
               525177,
               164509
            ]
         },
         "properties":{
            "CASENO":"CASE04393",
            "CASE_TYPE":"litterBin",
            "ASSETID":"200",
            "NOTES":"Bin full"
         }
      }
   ]
}
</script>
Advertisement