I have a shapefile of points, defined by X and Y coordinates, ad the ID feature.
I have at least 3 different points with the same ID number.
I would like to define, for each ID, the shapefile of a circle that circumscribes the points.
How can this be done in python environment?
there is a library that does it: https://pypi.org/project/miniball/
it's pretty forward to integrate in standard pandas pattern https://pandas.pydata.org/pandas-docs/stable/user_guide/groupby.html
solution really reduces to this:
def circle(points):
p, r = miniball.get_bounding_ball(np.array([points.x, points.y]).T)
return shapely.geometry.Point(p).buffer(math.sqrt(r))
col = "group"
# generate circles around groups of points
gdf_c = cities.groupby(col, as_index=False).agg(geometry=("geometry", circle))
with sample example and visualisation, circles do become distorted due to epsg:4326 projection limitations
full working example
import geopandas as gpd
import numpy as np
import shapely
import miniball
import math
import pandas as pd
cities = gpd.read_file(gpd.datasets.get_path("naturalearth_cities"))
world = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
# a semi-synthetic grouping of cities
world["size"] = world.groupby("continent")["pop_est"].apply(
lambda d: pd.cut(d, 2, labels=list("ab"), duplicates="drop").astype(str)
)
cities = cities.sjoin(world.loc[:, ["continent", "iso_a3", "size", "geometry"]])
cities["group"] = cities["continent"] + cities["size"]
def circle(points):
p, r = miniball.get_bounding_ball(np.array([points.x, points.y]).T)
return shapely.geometry.Point(p).buffer(math.sqrt(r))
col = "group"
# generate circles around groups of points
gdf_c = cities.groupby(col, as_index=False).agg(geometry=("geometry", circle))
# visualize it
m = cities.explore(column=col, height=300, width=600, legend=False)
gdf_c.loc[~gdf_c["geometry"].is_empty].explore(
m=m, column=col, marker_kwds={"radius": 20}, legend=False
)
output
I have just started learinig Geopandas lib in Python.
I have a dataset with Lat(E) and Lon(N) of car accidents in Belgrade.
I want to plot those dots on the map of Belgrade.
This is my code:
import pandas as pd
import geopandas as gpd
import matplotlib.pyplot as plt
pd.set_option('display.max_rows', 150)
pd.set_option('display.max_columns', 200)
pd.set_option('display.width', 5000)
# reading csv into geopandas
geo_df = gpd.read_file('SaobracajBeograd.csv')
geo_df.columns = ["ID", "Date,Time", "E", "N", "Outcome", "Type", "Description", "geometry"]
geo_df.geometry = gpd.points_from_xy(geo_df.E, geo_df.N)
#print(geo_df)
# reading built in dataset for each city
world_cities = gpd.read_file(gpd.datasets.get_path('naturalearth_cities'))
# I want to plot geometry column only for Belgrade
ax = world_cities[world_cities.name == 'Belgrade'].plot(figsize=(7, 7), alpha=0.5, edgecolor='black')
geo_df.plot(ax=ax, color='red')
plt.show()
This is the result that I get:
How can I prettify this plot, so that I can see the map of the city ( with streets if possible, in color) and with smaller red dots?
as per comments, folium provides base map of overall geometry
have added two layers
Belgrade, I have obtained this geometry from osmnx this is beyond the scope of this question so have just included the polygon as a WKT string
the points that you provided via link in comments
from pathlib import Path
import pandas as pd
import geopandas as gpd
import shapely
import folium
# downloaded data
df = pd.read_csv(
Path.home().joinpath("Downloads/SaobracajBeograd.csv"),
names=["ID", "Date,Time", "E", "N", "Outcome", "Type", "Description"],
)
# create geodataframe, NB CRS
geo_df = gpd.GeoDataFrame(
df, geometry=gpd.points_from_xy(df["E"], df["N"]), crs="epsg:4386"
)
# couldn't find belgrade geometry, used osmnx and simplified geometry as a WKT string
belgrade_poly = shapely.wkt.loads(
"POLYGON ((20.2213764 44.9154621, 20.2252450 44.9070062, 20.2399466 44.9067193, 20.2525385 44.8939145, 20.2419942 44.8842235, 20.2610016 44.8826597, 20.2794675 44.8754192, 20.2858284 44.8447802, 20.2856918 44.8332410, 20.3257447 44.8342507, 20.3328068 44.8098272, 20.3367239 44.8080890, 20.3339619 44.8058144, 20.3353253 44.8011005, 20.3336310 44.8003791, 20.3360230 44.7898245, 20.3384687 44.7907875, 20.3405086 44.7859144, 20.3417344 44.7872272, 20.3474466 44.7713203, 20.3509860 44.7687822, 20.3398029 44.7558716, 20.3220093 44.7448572, 20.3160895 44.7387338, 20.3235092 44.7345531, 20.3359605 44.7308053, 20.3437350 44.7301552, 20.3450306 44.7243651, 20.3497410 44.7209764, 20.3521450 44.7143627, 20.3633795 44.7046060, 20.3830709 44.7030441, 20.3845248 44.7011631, 20.3847991 44.7032182, 20.3924066 44.7036702, 20.4038881 44.6984458, 20.4097684 44.6992834, 20.4129839 44.7024603, 20.4192098 44.7021308, 20.4217436 44.7034920, 20.4251744 44.6976337, 20.4279418 44.6980838, 20.4313251 44.6940680, 20.4358368 44.6933579, 20.4402665 44.6905161, 20.4452138 44.6910160, 20.4495428 44.6880459, 20.4539572 44.6888231, 20.4529809 44.6911331, 20.4550753 44.6919188, 20.4534174 44.6929137, 20.4571253 44.6957696, 20.4570013 44.7008391, 20.4614601 44.7027894, 20.4646634 44.7018970, 20.4674388 44.7050131, 20.4753542 44.7039532, 20.4760757 44.7050260, 20.4802055 44.7033479, 20.4867635 44.7061539, 20.4983359 44.7022445, 20.5049892 44.7021663, 20.5071809 44.7071295, 20.5027682 44.7154832, 20.5028502 44.7217294, 20.5001912 44.7225288, 20.5007294 44.7251513, 20.5093727 44.7271542, 20.5316662 44.7248060, 20.5385861 44.7270519, 20.5390058 44.7329843, 20.5483761 44.7280993, 20.5513810 44.7308508, 20.5510751 44.7340860, 20.5483958 44.7345580, 20.5503614 44.7352316, 20.5509440 44.7434333, 20.5416617 44.7521169, 20.5358563 44.7553171, 20.5348919 44.7609694, 20.5393015 44.7624855, 20.5449353 44.7698750, 20.5490005 44.7708792, 20.5488362 44.7733456, 20.5647717 44.7649237, 20.5711431 44.7707818, 20.5772388 44.7711074, 20.5798915 44.7727751, 20.5852472 44.7808647, 20.5817268 44.7826053, 20.5823183 44.7845765, 20.5792147 44.7843299, 20.5777701 44.7872565, 20.5744279 44.7854098, 20.5740215 44.7886805, 20.5693220 44.7911579, 20.5655386 44.7906451, 20.5635444 44.7921747, 20.5598333 44.7901679, 20.5536143 44.7898282, 20.5502434 44.7909478, 20.5435002 44.8022967, 20.5424780 44.8073064, 20.5474459 44.8103678, 20.5530335 44.8102412, 20.5652728 44.8188428, 20.5738545 44.8279189, 20.5724006 44.8315147, 20.5776931 44.8371416, 20.5765153 44.8378971, 20.5863097 44.8427122, 20.5826128 44.8462544, 20.5762290 44.8486489, 20.5825139 44.8520894, 20.5953933 44.8552493, 20.6206689 44.8543410, 20.6212821 44.8560293, 20.6173687 44.8574761, 20.5961883 44.8615803, 20.5928447 44.8609861, 20.5911876 44.8626994, 20.6019440 44.8670619, 20.6196285 44.8673213, 20.6232109 44.8693710, 20.6164092 44.8815202, 20.6152606 44.8895682, 20.5777643 44.8860527, 20.5311826 44.8712209, 20.5230234 44.8646244, 20.5226088 44.8685278, 20.5187616 44.8654899, 20.5197414 44.8694015, 20.5132944 44.8687179, 20.5076686 44.8735038, 20.5065584 44.8670548, 20.4991594 44.8719635, 20.4938631 44.8734651, 20.4821047 44.8723679, 20.4737899 44.8677144, 20.4661802 44.8592493, 20.4594505 44.8560945, 20.4600397 44.8546034, 20.4650988 44.8535738, 20.4600110 44.8491680, 20.4623204 44.8477906, 20.4603705 44.8445375, 20.4711373 44.8342913, 20.4706338 44.8317839, 20.4498025 44.8343946, 20.4244846 44.8431449, 20.4138827 44.8526577, 20.3912248 44.8598333, 20.3749815 44.8683583, 20.3617778 44.8791076, 20.3436922 44.9103973, 20.3390650 44.9117584, 20.3011288 44.9426876, 20.2946156 44.9402419, 20.2960052 44.9381397, 20.2746476 44.9304194, 20.2703905 44.9345682, 20.2213764 44.9154621))"
)
# plot belgrade city limits
m = gpd.GeoDataFrame(geometry=[belgrade_poly], crs="epsg:4326").explore(name="Belgrade", height=300, width=500)
# plot the points, just for demo purposes plot outcomes as different colors
m = geo_df.explore(m=m, column="Outcome", cmap=["red","green","blue"], name="points")
# add layer control so layers can be switched on / off
folium.LayerControl().add_to(m)
m
supplementary update
Obtain Belgrade geometry
import osmnx as ox
gdf = ox.geocode_to_gdf({'city': 'Belgrade'})
It seems that you are not able to get a map of city using the dataset from world_cities.
For example, if you check
belgrade = world_cities[world_cities.name == 'Belgrade']
it returns following geopandas dataframe
name geometry
102 Belgrade POINT (20.46604 44.82059)
The geometry is in the form of Point which is basically longitude and latitude. The geometry must include polygon of some sort to get the shape of the city you want.
For example, if you extract the country from the world dataset as follows:
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
serbia = world[world.name == "Serbia"]
it returns the following geopandas dataframe for serbia
pop_est continent name iso_a3 gdp_md_est geometry
172 7111024 Europe Serbia SRB 101800.0 POLYGON ((18.82982 45.90887, 18.82984 45.90888...
As you see, the geometry is in the shape of polygon. Now you can plot the map using serbia.plot() to get the map of serbia, which looks as follows:
To get the map of the city, you need to first download the shape file of the city in the form of *.shp file along with other supporting files, and read the *.shp file as gpd.read_file("file.shp"). Only then you are able to plot the map of the required city.
Inspired by the example Plot precip with filled contours from this website I want to make a plot of yesterday's precipitation data, projected onto a map. The example from that website can, however, no longer be used as the data format of the precipitation data has changed.
My approach is as follows:
download the netCDF4-file from the National Weather Service website
open the netCDF4-file and extract the relevant information
create a map with Basemap
project the precipitation data onto the map
I guess my problem is that I do not understand the netCDF4-file format, and in particular the metadata, since the information about the grid origin of the precipitation data must be hidden somewhere in it.
My code looks as follows:
from datetime import datetime, timedelta
import netCDF4
import numpy as np
import matplotlib.pyplot as plt
import os.path
import urllib
from mpl_toolkits.basemap import Basemap
# set date for precipitation (1 day ago)
precip_date = datetime.utcnow() - timedelta(days=1)
precip_fname = 'nws_precip_1day_{0:%Y%m%d}_conus.nc'.format( precip_date )
precip_url = 'http://water.weather.gov/precip/downloads/{0:%Y/%m/%d}/{1}'.format( precip_date, precip_fname )
# download netCDF4-file if it does not exist already
if not os.path.isfile( precip_fname ):
urllib.urlretrieve( precip_url, precip_fname )
# read netCDF4 dataset and extract relevant data
precip_dSet = netCDF4.Dataset( precip_fname )
# spatial coordinates
precip_x = precip_dSet['x'][:]
precip_y = precip_dSet['y'][:]
# precipitation data (is masked array in netCDF4-dataset)
precip_data = np.ma.getdata( precip_dSet['observation'][:] )
# grid information
precip_lat0 = precip_dSet[ precip_dSet['observation'].grid_mapping ].latitude_of_projection_origin
precip_lon0 = precip_dSet[ precip_dSet['observation'].grid_mapping ].straight_vertical_longitude_from_pole
precip_latts = precip_dSet[ precip_dSet['observation'].grid_mapping ].standard_parallel
# close netCDF4 dataset
precip_dSet.close()
fig1, ax1 = plt.subplots(1,1, figsize=(9,6) )
# create the map
my_map = Basemap( projection='stere', resolution='l',
width=(precip_x.max()-precip_x.min()),
height=(precip_y.max()-precip_y.min()),
lat_0=30, # what is the correct value here?
lon_0=precip_lon0,
lat_ts=precip_latts
)
# white background
my_map.drawmapboundary( fill_color='white' )
# grey coastlines, country borders, state borders
my_map.drawcoastlines( color='0.1' )
my_map.drawcountries( color='0.5' )
my_map.drawstates( color='0.8' )
# contour plot of precipitation data
# create the grid for the precipitation data
precip_lons, precip_lats = my_map.makegrid( precip_x.shape[0], precip_y.shape[0] )
precip_xx, precip_yy = my_map( precip_lons, precip_lats )
# make the contour plot
cont_precip = my_map.contourf( precip_xx, precip_yy, precip_data )
plt.show()
This is how the output looks like (yes, for the final plot the color-levels have to be adjusted):
I know that this is a very specific question, so any suggestions/hints are greatly appreciated.
If I understand correctly you are able to make the plot but want hints on adding extras?
xarray is a fantastic toolbox for working with netCDF files. It works like pandas but for netCDF files and is a big improvement on 'netCDF4':
http://xarray.pydata.org/en/stable/
To specify specific contours you can input the levels:
cont_precip = my_map.contourf( precip_xx, precip_yy, precip_data,levels=[10,20,30]) # Edit for exact contours needed
If you wanted you can add a colorbar:
fig1.colorbar(cont_precip,ax=ax1)