7

I have a shapefile, which I converted into GeoPandas DataFrame. I would like to round the coordinates to 5 decimals.

Is there a method/function in GeoPandas that can do this?

My shapefile contains MultiPolygons.

Taras
  • 32,823
  • 4
  • 66
  • 137
Vinay
  • 231
  • 1
  • 4
  • 6
  • 2
    Be careful. Rounding of coordinates can damage polygon topology. Rounding degrees to 5 places could change coordinate placement by 1 meter. – Vince May 02 '19 at 18:08
  • I saw the precision table and decided 1 meter is good enough for my application. Hence 5 decimals. – Vinay May 02 '19 at 18:09
  • Except it doesn't work like that. If the data has centimeter resolution, coordinates may come closer than a meter, and the resulting truncation could cause polygon rings to touch. It takes a more sophisticated approach than just rounding coordinates. – Vince May 02 '19 at 18:18
  • 1
    But why? What do you gain from this? The object will still occupy the same size in memory, you might snap nodes together and break topology, you'll modify the area and perimeter of your features. Maybe you can do this validly for point features, but I'd never do this for lines or polygons, unless its for display purposes only (ie showing a table of vertex coordinates). – Spacedman May 02 '19 at 18:18
  • 1
    We are developing an interactive d3 visualization tool which shows all the US zips on a map with other features. Interaction speed is important in this case and I thought decreasing the precision will help in loading and displaying data fast. Is there any validity in my argument? @Vince - I really don't care if they overlap little bit. – Vinay May 02 '19 at 18:24

7 Answers7

4

I recommend researching TopoJSON and Mapshaper as these tools were created to intelligently simplify shapes, preserving topology. Both tools are written in JavaScript. Mapshaper has a precision option for the output. You can run Mapshaper through a website, mapshaper.org or download the command line tools.

Vince
  • 20,017
  • 15
  • 45
  • 64
klewis
  • 7,475
  • 17
  • 19
  • Can't upvote due to reputation but it works. – Vinay May 02 '19 at 19:01
  • Mapshaper is removing other properties in my shapefile and retaining only geometry. Is there a command to include properties? I remember this was not the case when I reduced a shapefile 6 months ago. – Vinay May 02 '19 at 19:26
  • This might help with the attributes, https://gis.stackexchange.com/questions/238204/prevent-mapshaper-stripping-out-data – klewis May 02 '19 at 19:53
  • I now tried Mapshaper for multiple times and it just keeps running and running over 6 hours + with the "-clean" flag (300mb geoJson). Whatever it's doing it sadly has no progress bar. – nonNumericalFloat Jan 13 '22 at 20:38
4

You can use the regex module to find the coordinates in a wkt representation of the geometries, round and load back:

import geopandas as gpd
from shapely.wkt import loads
import re

simpledec = re.compile(r"\d*\.\d+")
def mround(match):
    return "{:.5f}".format(float(match.group()))

shapefile = '/home/bera/GIS/data/test/polys.shp'
df = gpd.read_file(shapefile)
df.geometry = df.geometry.apply(lambda x: loads(re.sub(simpledec, mround, x.wkt)))
df.to_file('/home/bera/GIS/data/test/polys_round.shp')

See: Rounding using regular expressions

enter image description here

Or try this: Is it possible to round all coordinates in shapely?

BERA
  • 72,339
  • 13
  • 72
  • 161
  • The second method doesn't work if there are multi-polygons in geometry. Although we can identify them and loop it differently. – Vinay May 02 '19 at 19:49
2

Here's a solution which rounds the numbers directly without relying on regex. Built off of this one: https://gis.stackexchange.com/a/432720. The shapely.ops.transform function ensures it always works on all geometry types.

from shapely.ops import transform

def round_coordinates(geom, ndigits=2):

def _round_coords(x, y, z=None): x = round(x, ndigits) y = round(y, ndigits)

  if z is not None:
      z = round(x, ndigits)
      return (x,y,z)
  else:
      return (x,y)

return transform(_round_coords, geom)

import geopandas as gpd from shapely.geometry import Point

point_geoms = [Point(10.1234567, 20.1234567), Point(30.5123456, 40.59876543)] gdf = gpd.GeoDataFrame({'id':[1,2]}, geometry=gpd.GeoSeries(point_geoms))

gdf['geometry'] = gdf.geometry.apply(round_coordinates, ndigits=1) gdf.to_wkt()

Shawn
  • 1,817
  • 9
  • 21
1

Since december 2022, shapely.set_precision has been available for this purpose. This function will round the coordinates but will also make sure the output is a valid geometry, as with polygons just rounding coordinates can easily lead to self-intersections,...:

import shapely

Round the coordinates to 5 decimals

gdf.geometry = shapely.set_precision(gdf.geometry, grid_size=0.00001)

In GeoPandas 1.0, planned to be released 31 march 2024, set_precision will also be available like this:

# Round the coordinates to 5 decimals
gdf.geometry = gdf.geometry.set_precision(grid_size=0.00001)
Pieter
  • 1,876
  • 7
  • 9
0

I encountered a similar issue and searching for a solution! How can I round the coordinates of the "SHAPE" column in an SDF created with pandas.DataFrame.spatial? The "round function would not work against "SHAPE" because it is not a number.

import pandas as pd
df = pd.DataFrame.spatial.from_featureclass("c:\\test.gdb\testFeatureClass")
df.round({"SHAPE": 5}) # TypeError: unsupported operand type(s) for *: 'Polyline' and 'float'

I am trying to use "merge" operation on 2 sdf to identify changes made in geometry. However, I found that "identical" feature from gdb and agol has tinny difference in coordinates (9th or 10th decimal in feet). I am thinking to use round the decimal of coordinates to 7 or 8 place.

Kadir Şahbaz
  • 76,800
  • 56
  • 247
  • 389
0

I am not an expert. But the code below does the job for me. Consider 'gdf' is a geopandas dataframe.

gdf_rounded = gdf.__round__(5)
rmj
  • 387
  • 3
  • 13
0
gdf_rounded = gdf.round(5)

Rounds the coordinates to 5 decimal places

wfgeo
  • 3,538
  • 2
  • 24
  • 47
  • I think the round method is inherited from pandas, since if you run it on the geometry column of a geopandas geodataframe it throws an error. – Shawn Nov 16 '22 at 14:05
  • Could be the case, I noticed that __round__ worked in my case, as per a different answer here. My geodataframe in this case has only a geometry column, but it dud reduce the coordinate precision properly – wfgeo Nov 16 '22 at 15:38