Search code examples
geojsonjq

Conditionals inside a larger jq expression to transform input into valid geojson


I have a JSON input that is nearly-but-not-quite correct geojson, and I'm attempting to use jq to transform it into a correct format. My input is using a custom geometry type of "Path" on some items, and inside the coordinates of these items it's storing some extra data (line curve data for rendering to SVG). I don't want to drop this data, so my goal has been to change all geometry items of type "Path" into a "LineString", and strip out the extra coordinates in these items into a "properties" object (on the geometry item) to preserve the data.

Here's a small example of the input:

{
  "type": "FeatureCollection",
  "features": [
    {
      "id": 16683828,
      "properties": {
        "facility": "hallway"
      },
      "type": "Feature",
      "geometry": {
        "type": "Path",
        "coordinates": [
          [
            0,
            379.64,
            289.412
          ],
          [
            3,
            379.629,
            289.768,
            379.346,
            290.059,
            378.986,
            290.066
          ],
          [
            1,
            373.156,
            290.066
          ],
          [
            1,
            373.156,
            298.5
          ],
          [
            1,
            373.156,
            299.469
          ],
          [
            4
          ]
        ]
      }
    }
  ]
}

Here's roughly what I'd like to transform this into:

{
  "type": "FeatureCollection",
  "features": [
    {
      "properties": {
        "facility": "hallway",
        "id": 16683828
      },
      "type": "Feature",
      "geometry": {
        "type": "LineString",
        "properties": {
          "curves": [
            {
              "coordinate_position": 1,
              "control_points": [379.629, 289.768, 379.346, 290.059]
            }
          ]
        },
        "coordinates": [
          [
            379.64,
            289.412
          ],
          [
            378.986,
            290.066
          ],
          [
            373.156,
            290.066
          ],
          [
            373.156,
            298.5
          ],
          [
            373.156,
            299.469
          ]
        ]
      }
    }
  ]
}

The part that's tripping me up is using conditionals inside of a larger jq formatting expression. For each feature, I'm trying to check if geometry.type == "Path", and if so, strip the first item from each coordinate (I don't need it), and then move all but the last two items from the coordinate into a object in a properties.curves array, noting the position within the "coordinates" array, and the extra data. If the geometry.type != "Path", I just want to copy the geometry item to my output.

Here's the script I have so far, including the incorrect conditional logic beginning at geometry: {:

cat input.json | jq '. | 
{
    type: .type,
    features: [
        .features[] | {
            type: .type,
            properties: {
                display_name: .properties.display_name,
                id: .id
            }
            geometry: {
                if .geometry.type == "Path" then
                    type: "LineString"
                else
                    type: .geometry.type
                end
            }
        }
    ]
}'

Of course what's not working here is the conditional directly inside an object. My best guess is that I need to use more jq pipes but I just haven't been able to figure out how to wrangle it into the correct format.


Solution

    1. Many would suggest writing "jq ... FILE" instead of 'cat FILE | jq ...'

    2. You don't need the initial '. |'

    3. Here is a variant of your jq filter that produces the results shown below:

     

    {
        type: .type,
        features: [
            .features[] | {
                type: .type,
                properties: {
                    display_name: .properties.facility,
                    id: .id
                },
                geometry: {
                    type: (if .geometry.type == "Path" then "LineString"
                           else .geometry.type
                           end)
                }
            }
        ]
    }
    

    Output:

    {
      "type": "FeatureCollection",
      "features": [
        {
          "type": "Feature",
          "properties": {
            "display_name": "hallway",
            "id": 16683828
          },
          "geometry": {
            "type": "LineString"
          }
        }
      ]
    }