Search code examples
pythonmatplotlibgeopandas

How can I rotate a matplotlib map?


Starting with a shapefile I obtained from https://s3.amazonaws.com/nyc-tlc/misc/taxi_zones.zip, I'd like to plot the borough of Manhattan, and have outlines for each taxi-zone.

This code rotates each individual taxi zone individually instead of all at once.

import geopandas as gpd
from matplotlib import pyplot as plt


fname = "path_to_shapefile.shp"
df = gpd.read_file(fname)
df = df[df['borough'] == "Manhattan"]
glist = gpd.GeoSeries([g for g in df['geometry']])
glist = glist.rotate(90)
glist.plot()

enter image description here

[EDIT] I have further refined this to be able to rotate the image programmatically. However, if I add a legend, then that is also rotated, which is not desirable. Still looking for a better solution. Note, there is also this stackoverflow post (How can I rotate a matplotlib plot through 90 degrees?), however, the solutions that rotate the plot, and not the image, only work with 90 degree rotations.

import geopandas as gpd
from matplotlib import pyplot as plt

import numpy as np
from scipy import ndimage
from matplotlib import transforms


fname = "path_to_shapefile.shp"
df = gpd.read_file(fname)
df = df[df['borough'] == "Manhattan"]
df.plot()
plt.axis("off")
plt.savefig("test.png")

img = plt.imread('test.png')

rotated_img = ndimage.rotate(img, -65)
plt.imshow(rotated_img, cmap=plt.cm.gray)
plt.axis('off')
plt.show()

[EDIT2]

A simple modification to the answer given below by @PMende solved it.

df = gpd.read_file(fname)
df = df[df['borough'] == "Manhattan"]
glist = gpd.GeoSeries([g for g in df['geometry']])
glist = glist.rotate(-65, origin=(0,0))
glist.plot()

The key was rotating all of the objects around a single point, instead of around their individual origins.

[EDIT 3] If anyone is trying to do this, and needs to save the resulting rotated geoseries to a dataframe (say for instance, to color the geometry based on an additional column), you need to create a new one, simply writing

df['geometry'] = glist

does not work. I'm not sure why at the moment. However, the following code worked for me.

new_dataframe = gpd.GeoDataFrame(glist)
new_dataframe = new_dataframe.rename(columns={0:'geometry'}).set_geometry('geometry')
new_dataframe.plot()

enter image description here


Solution

  • If I'm understanding GeoPandas' documentation correctly, you can specify the origin of the rotation of each of your geometries (which by default is the center of each geometry). To get your desired behavior, you can rotate each shape about the same origin.

    For example:

    import geopandas as gpd
    from matplotlib import pyplot as plt
    
    
    fname = "path_to_shapefile.shp"
    df = gpd.read_file(fname)
    df = df[df['borough'] == "Manhattan"]
    
    center = df["geometry"].iloc[0].centroid()
    glist = gpd.GeoSeries([g for g in df['geometry']])
    glist = glist.rotate(90, origin=center)
    
    glist.plot()
    

    I can't test this myself, but it should hopefully get you started in the right direction.

    (Though I also agree with @martinfeleis' point about not necessarily wanting to rotate the geometry, but rather the plot.)