32

I would like to return the lat, lon or x, y from the point object in the following series as I plan to link them with an API I built that links OSM and NetworkX. The centroids will be added as new nodes for network analysis.

gp.GeoSeries(zones.centroid).x, and gp.GeoSeries(zones.centroid).y as outlined in docs raise the following error:

AttributeError: 'GeoSeries' object has no attribute 'x'

Modifying things a bit and printing list(gp.GeoSeries(zones.centroid)) return thousands of shapely points of the following format:

[... <shapely.geometry.point.Point object at 0x0000000024035940>,
<shapely.geometry.point.Point object at 0x0000000024035978>, 
<shapely.geometry.point.Point object at 0x00000000240359B0>, 
<shapely.geometry.point.Point object at 0x00000000240359E8>, 
<shapely.geometry.point.Point object at 0x0000000024035A20>, 
<shapely.geometry.point.Point object at 0x0000000024035A58>, 
<shapely.geometry.point.Point object at 0x0000000024035A90>, 
<shapely.geometry.point.Point object at 0x0000000024035AC8>]

The code I'm using is the following:

import geopandas as gp

zones = gp.GeoDataFrame.from_file(shp_file)

for index, row in zones.iterrows():
    print index, gp.GeoSeries(zones.centroid)

# result:
# 9700022.00    POINT (-122.8196050489696 54.00617624128658)
# 9700023.00    POINT (-122.7474362519174 53.99998921974029)
# 9700100.00    POINT (-121.4904983300892 53.98447191612864)
# 9700101.00    POINT (-122.5513619751679 53.73999791511078)
# 9700102.00    POINT (-123.0624037191615 53.62317549646422)
# 9700103.00    POINT (-123.0848175548173 54.05921695782788)

How can I return the x, y from the GeoPandas POINT object?

8 Answers 8

33

Ran into this problem myself. If you want the x and y as separate GeoDataFrame columns, then this works nicely:

gdf["x"] = gdf.centroid.map(lambda p: p.x)
gdf["y"] = gdf.centroid.map(lambda p: p.y)

Starting with GeoPandas 0.3.0, you can use the provided x and y properties instead:

gdf["x"] = gdf.centroid.x
gdf["y"] = gdf.centroid.y
19

Leaving the rest below, but the main thing was accessing the geometry properly. If iterating over rows, e.g. for index, row in zones.iterrows(): you can simply use row.geometry.centroid.x and row.geometry.centroid.y. Geometry is a special column included in a GeoDataFrame, so every row has a geometry attribute.
You are accessing that attribute, which contains a shapely object. That shapely object will have an attribute, centroid that, in turn contains a shapely.geometry.Point, which has attributes x and y, finally giving you the properties you want.


(This part was the original effort to get to x,y with map and shapely.geometry.Point.)
I am going to assume you want a list of (x, y) tuples? Create a quick accessor function for the x and y attributes on a Point and use map.

Edit: Okay, figured out that you may be accessing the geometry in the GeoDataFrame in an incorrect way. Geometry is a column in your GeoDataFrame, which by itself produces a series. Calling centroid on that column should give you a new GeoSeries of only those centroids. I suspect the way you were going about things was taking the centroid of every vertex in each polygon. Still cannot test this since I cannot install GeoPandas right now.

def getXY(pt):
    return (pt.x, pt.y)
centroidseries = zones['geometry'].centroid
centroidlist = map(getXY, centroidseries)

or if you want two separate lists of x and y coordinates

def getXY(pt):
    return (pt.x, pt.y)
centroidseries = zones['geometry'].centroid
x,y = [list(t) for t in zip(*map(getXY, centroidseries))]

Alternately, you should also be able to use zones.geometry.centroid instead of zones['geometry'].centroid. Either way, I think calling zones.centroid may be returning a GeoDataFrame instead of a GeoSeries, giving you the unexpected output when you wrap it in another GeoSeries.

5
  • 1
    Whoops, I wrote my maps backwards. Fixing now. I just got geopandas.org unblocked, since clearly GeoSeries is interating more than just the centroids. Commented Oct 16, 2015 at 19:59
  • 1
    Although the code works, it seems that list(...) is returning a lot more points than what I need. I have 30 zones so I should only have 30 centroids. when expanding to list(...) it's returning thousands of points
    – dassouki
    Commented Oct 16, 2015 at 20:11
  • 1
    Tried some work on this. Still tricky without being able to install geopandas, but trying to follow the documentation examples. Commented Oct 16, 2015 at 20:24
  • 1
    Sounds good. The reason I'm using a dataframe is that i'm grouping my centroids by a field in my shape file as follows: for index, row in zones.iterrows(): if row['CMANAME'] == u'zone_name':
    – dassouki
    Commented Oct 16, 2015 at 20:26
  • 1
    Got it... well, (row.geometry.centroid.x, row.geometry.centroid.y) should get you the x, y values if you are iterating over all the rows anyway. Basically that is taking the geometry column of the row (a polygon), accessing the centroid (a point), and then getting the x and y attributes of that point. Not as pretty as mapping everything, but could get the job done. Commented Oct 16, 2015 at 20:31
6

This has been made easier as of GeoPandas 0.3.0.

You can now access x and y of shapely Points inside a geopandas GeoSeries using your_GeoDataFrame.geometry.x and your_GeoDataFrame.geometry.y

(Note : I'm using python 3.6.1, not sure about behavior in 2.7, sorry)

Source on github

5

The solution to extract the center point (latitude and longitude) from the polygon and multi-polygon.

import geopandas as gpd
df = gpd.read_file(path + 'df.geojson')
#Find the center point
df['Center_point'] = df['geometry'].centroid
#Extract lat and lon from the centerpoint
df["lat"] = df.Center_point.map(lambda p: p.x)
df["long"] = df.Center_point.map(lambda p: p.y)
1
  • 1
    Just wanted to point out that longitude is the X value and latitude is the Y value. Commented Oct 12, 2022 at 23:38
1

if you just want a numpy array of centroids:

centroids = np.vstack([df.centroid.x, df.centroid.y]).T

or as a dataframe with extra columns (i.e name for example):

pd.DataFrame(np.vstack([df.name, df.centroid.x, df.centroid.y]).T, columns=['name', 'x', 'y'])

ex.

   name                                           geometry
0     1  POLYGON ((0.00000 1.00000, 0.00000 0.00000, 1....
1     2  POLYGON ((2.00000 1.00000, 2.00000 0.00000, 3....

# ==>
# np.vstack([df.centroid.x, df.centroid.y]).T
#    array([[0.5, 0.5],
#           [2.5, 0.5]])
0

I ran into a similar problem, only using polygon geometries and found this solution worked well for me, mind you it's using Python-3.6 so it may not work for Python-2.7.

import geopandas as gpd

zones = gpd.read_file('file_to_read.shp')

for i in range(0,len(zones)):
    zones.loc[i,'centroid_lon'] = zones.geometry.centroid.x.iloc[i]
    zones.loc[i,'centroid_lat'] = zones.geometry.centroid.y.iloc[i]
0

Here's a quick

df = pd.read_csv('data/hospitals.csv')
df.columns # 'Ownership', 'Lat', 'Long'

from shapely.geometry import Point
df['geometry'] = df.apply(lambda row: Point(row.Long,row.Lat),axis=1)

Then recreate the dataframe with geopandas

gdf = gpd.DataFrame(df) # creates geodataframe with Point geometry
1
  • Ok. Let me go ahead and delete it. Commented Apr 25, 2021 at 19:15
0

This one worked for me:

import numpy as np

test = geopandas.read_file("test.geojson")`

centroids = np.column_stack((test.centroid.x, test.centroid.y)))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.