Search code examples
pythonpandasgeojsongeopandasshapely

Convert LineString to Polygon using Python


I am trying to convert geometry from type: LineString into type:Polygon where each line of data is a list of coordinates and at the same time I am trying to add 2 miles radius for each coordinates within list as buffer and output should be polygons. Please see sample data as below:

{

"type": "FeatureCollection",

"name": "Sample_lines",

"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },

"features": [

{ "type": "Feature", "properties": { "OBJECTID": 123, "GLOBAL_ID": "8CAB8A", "IDENT": "41",  "TYPE": "N",  "Shape__Length": 0.2733 }, "geometry": { "type": "LineString", "coordinates": [ [ -112.400011882673994, 41.0833390325461, 0.0 ], [ -112.56667894652, 41.300005042600802, 0.0 ] ] } },

{ "type": "Feature", "properties": { "OBJECTID": 124, "GLOBAL_ID": "9ACAVB", "IDENT": "45",  "TYPE": "N",  "Shape__Length": 0.1573 }, "geometry": { "type": "LineString", "coordinates": [ [ -112.56667894652, 41.300005042600802, 0.0 ], [ -112.650011982188005, 41.4333400501312, 0.0 ] ] } },

{ "type": "Feature", "properties": { "OBJECTID": 125, "GLOBAL_ID": "5ACBFA", "IDENT": "48",  "TYPE": "N",  "Shape__Length": 0.4599 }, "geometry": { "type": "LineString", "coordinates": [ [ -112.650011982188005, 41.4333400501312, 0.0 ], [ -113.100012081374004, 41.5000060205737, 0.0 ] ] } }

]

}

This is what I have tried so far and it is throwing an error as follows:

buffer = gpd.points_from_xy([(x,y)])
TypeError: points_from_xy() missing 1 required positional argument: 'y'

.

Please see below code what I attempted to do:


import json
import geopandas as gpd
from shapely.geometry import MultiPolygon

# Load geojson 
with open('Sample_lines.geojson') as f:
    gj = json.load(f)

features = []
for f in gj['features']:

    coords = f['geometry']['coordinates']
    print(coords[0][0])
    print(coords[0][1])
    print(tuple(coords[0]))
    print(coords)
    #quit()
    # Buffer points along line
    buffers = []
    for x,y,z in tuple(coords):
        buffer = gpd.points_from_xy([(x,y)])
        buffer = buffer.to_crs(epsg=4326)
        buffer = buffer.buffer(2)
        buffers.append(buffer)
        
    # Create multipolygon from buffers    
    mpolygon = MultiPolygon(buffers)
    
    # Create feature
    features.append(
        {'geometry': gpd.GeoSeries(mpolygon).__geo_interface__,
         'properties': f['properties']}
    )

# Output new GeoJSON    
new_gj = {
  "type": "FeatureCollection",
  "features": features
}

# output in .GeoJSON

with open('lines2Polygon.geojson','w') as f:
    dump(new_gj, f)

print(new_gj)

Where am I going wrong here? Thank you in Advance for your time and support.


Solution

  • There were some errors:

    • the coordinate arrays should not be casted to tuple, they are ready to be looped over already
    • you should pass the crs the points are in in points_from_xy
    • to be able to do operations like buffers, your data should be in a projected crs. The lines you are working with are in the USA, so I chose a CRS that according to google is used a lot in the USA: EPSG:2163.
    • distances should be in metric system (meter), so 1 mile is 2 * 1609.34 meter

    The multipolygon created could be invalid if the individual buffers would overlap. If you use shapely.union_all on the individual buffers you can avoid overlaps. I included it like that in the script below.

    The following script runs:

    from pathlib import Path
    import json
    import geopandas as gpd
    from matplotlib import pyplot as plt
    import shapely
    from shapely import plotting
    
    # Load geojson
    with open(Path(__file__).with_suffix(".geojson")) as f:
        gj = json.load(f)
    
    features = []
    for f in gj["features"]:
        coords = f["geometry"]["coordinates"]
        print(coords[0][0])
        print(coords[0][1])
        print(tuple(coords[0]))
        print(coords)
        # quit()
        # Buffer points along line
        buffers = []
        for x, y, z in coords:
            buffer = gpd.points_from_xy([x], [y], crs=4326)
            buffer = buffer.to_crs(epsg=2163)
            buffer = buffer.buffer(2*1609.34)
            buffers.append(buffer)
    
        # Create multipolygon from buffers
        # mpolygon = shapely.MultiPolygon(buffers)
        mpolygon = shapely.union_all(buffers)
        plotting.plot_polygon(mpolygon)
    
        # Create feature
        features.append(
            {
                "geometry": gpd.GeoSeries(mpolygon).__geo_interface__,
                "properties": f["properties"],
            }
        )
    
    # Output new GeoJSON
    new_gj = {"type": "FeatureCollection", "features": features}
    
    # output in .GeoJSON
    with open("lines2Polygon.geojson", "w") as f:
        json.dump(new_gj, f)
    
    print(new_gj)
    plt.show()