I am trying to load in a shapefile with the use of geopandas. I have read the file in, but when I go to add the layer to the ipyleaflet map, an error comes up that says 'GeoDataFrame' object has no attribute 'model_id'.
I have tried many files and it just will not read with the geodataframe.
from ipyleaflet import Map, basemaps
fire_was = gpd.read_file('fire_wa.shp')
#add map
center = [47.409824923593575,-120.43401014843751]zoom = 7
m = Map(basemap=basemaps.OpenStreetMap.Mapnik, center=center, zoom=zoom)
m.add_layer(fire_was)
m
Any ideas?
I figured it out. The example I had seen did not show the below but checked more of the documentation of ipyleaflet. You must use GeoData to load the geodataframe.
from ipyleaflet import Map, GeoData, basemaps, LayersControl
fire_was = gpd.read_file('fire_wa.shp') firewa = GeoData(geo_dataframe = fire_was)
m = Map(center =(47.409824923593575,-120.43401014843751), zoom =7,basemap= basemaps.Esri.WorldTopoMap)
m.add_layer(firewa)
m
Related
I want to clip one raster based on the extent of another (smaller) raster. First I determine the coordinates of the corners of the smaller raster using
import rasterio as rio
import gdal
from shapely.geometry import Polygon
src = gdal.Open(smaller_file.tif)
ulx, xres, xskew, uly, yskew, yres = src.GetGeoTransform()
lrx = ulx + (src.RasterXSize * xres)
lry = uly + (src.RasterYSize * yres)
geometry = [[ulx,lry], [ulx,uly], [lrx,uly], [lrx,lry]]
This gives me the following output geometry = [[-174740.0, 592900.0], [-174740.0, 2112760.0], [900180.0, 2112760.0], [900180.0, 592900.0]]. (Note that the crs is EPSG: 32651).
Now I would like to clip the larger file using rio.mask.mask(). According to the documentation, the shape variable should be GeoJSON-like dict or an object that implements the Python geo interface protocol (such as a Shapely Polygon). Therefore I create a Shapely Polygon out of the variable geometry, using
roi = Polygon(geometry)
Now everything is ready to use the rio.mask() function.
output = rio.mask.mask(larger_file.tif, roi, crop = True)
But this gives me the following error
TypeError: 'Polygon' object is not iterable
What do I do wrong? Or if someone knows a more elegant way to do it, please let me know.
(Unfortunately I cannot upload the two files since they're too large)
I found your question when I needed to figure out this kind of clipping myself. I got the same error and fixed it the following way:
rasterio.mask expects a list of features, not a single geometry. So the algorithm wants to run masking over several features bundled in an iterable (e.g. list or tuple) so we need to pass it our polygon within a list (or tuple) object.
The code you posted works after following change:
roi = [Polygon(geometry)]
All we have to do is to enclose the geometry in a list/tuple and then rasterio.mask works as expected.
I am trying to overlay a Voronoi diagram over a folium web map. I am creating the voronoi map with SciPy and then converting it to Json. When I generate the map, the entire map is there except for the Voronoi layer, but the layer does show up in Layer Control.
I'm guessing that the error is somewhere in how the Json file is being created. I've been searching but without an error, I haven't been able to find anything
Folium map generation
m = folium.Map(location=[43.521, -120.587],
zoom_start = 7.45,
tiles = 'Mapbox Bright')
tooltip = 'Click for detailed information'
for point in range(len(locationList)):
folium.Marker(locationList[point], popup = labels[point], icon = folium.Icon(color = colors[point]), parse_html=True).add_to(m)
m.save('index.html')
Voronoi diagram generation
### Create volonai map
points = np.array(df[["geo_long","geo_lat"]].values.tolist()) # Create points, each point is a hospital
vor = Voronoi(points) #Create voronoi object
voronoi_plot_2d(vor) # Create voronoi plot object
Conversion to Json
vorJSON = open('libVor.json', 'w')
point_voronoi_list = []
feature_list = []
for region in range(len(vor.regions)-1):
vertex_list = []
for x in vor.regions[region]:
if x == -1:
break;
else:
vertex = vor.vertices[x]
vertex = (vertex[1], vertex[0])
vertex_list.append(vertex)
polygon = Polygon([vertex_list])
feature = Feature(geometry=polygon, properties={})
feature_list.append(feature)
feature_collection = FeatureCollection(feature_list)
print (feature_collection, file=vorJSON)
vorJSON.close()
Adding layer to the map
vorGeoJson = json.load(open('libVor.json'))
folium.GeoJson(vorGeoJson,
name = 'geojson'
).add_to(m)
folium.LayerControl().add_to(m)
print(m)
m.save(outfile='libVor.html')
My goal is to get the polygons to overlay on the web map
I had exact same issue when trying to plot geoJSON files on my folium map. I figured out my issue was the absence of/wrong coordinate system.
Since I was converting my data from shapefile to geoJSON I used QGIS to project the data into right coordinate system (I believe that EPSG:4326 - WGS84 usually works).
I am new to shapefiles and mapping in python so I was hoping to get some help with overlaying data points from a shapefile on a density map.
To be honest, I am a beginner with mapping and reading in shapefiles so what I have so far not much.
I have started off using pyshp but if there are better packages out there to do this then I would love any feedback.
The following code is to create the base map of the LA area:
def get_base_map(rides_clean):
return folium.Map(locations=[rides_clean.start_lat.mean(),
rides_clean.start_lon.mean()],
zoom_start = 20, tiles = 'cartodbpositron')
The following code is to create the density/heat map:
from folium import plugins
stationArr = rides_clean[['start_lat', 'start_lon']][:40000].as_matrix()
get_base_map(rides_clean).add_child(plugins.HeatMap(stationArr,
radius=40, max_val=300))
The following code is the same heat map but with route lines added:
(draw_route_lines(get_base_map(rides_clean),
routedf_vol)).add_child(plugins.HeatMap(stationArr, radius=40,
max_val=300))
I want to see data points from the shapefile shown as markers on top of the density plot.
It is possible to do this with pyshp. I've only ever used Matplotlib to plot shapefile points on a map, but this method will create two arrays which will be the x and y coordinates of each point you'd like to plot. The first snippet is used if you have multiple shapes in your shapefile, while the second can be used if you only have one shape.
import shapefile
import numpy as np
sf = shapefile.Reader('/path/to/shapefile')
point_list = []
for shape in sf:
temp = shape.points()
point_list.append(temp)
point_list = np.array(point_list)
x = point_list[:,0]
y = point_list[:,1]
And for a shapefile with only a single shape:
import shapefile
import numpy as np
sf = shapefile.Reader('/path/to/shapefile')
point_list = np.array(sf.shape(0).points)
x = point_list[:,0]
y = point_list[:,1]
You can tell how many shapes are in your shapefile using sf.shapes() and it will print a list detailing all the different shapes. From your question it appeared you were wanting to plot it as points on the marker rather than lines, sorry if this is not the case.
I have this code:
import pandas as pd
import numpy as np
from geopandas import GeoDataFrame
import geopandas
from shapely.geometry import LineString, Point
import matplotlib.pyplot as plt
import contextily
''' Do Something'''
df = start_stop_df.drop('track', axis=1)
crs = {'init': 'epsg:4326'}
gdf = GeoDataFrame(df, crs=crs, geometry=geometry)
ax = gdf.plot()
contextily.add_basemap(ax)
ax.set_axis_off()
plt.show()
Basically, this generates a background map that is in Singapore. However, when I run it, I get the following error: HTTPError: Tile URL resulted in a 404 error. Double-check your tile url:http://tile.stamen.com/terrain/29/268436843/268435436.png
However, it still produces this image:
How can I change the Tile URL? I would still like to have the map of Singapore as the base layer.
EDIT:
Also tried including this argument to add_basemap:
url ='https://www.openstreetmap.org/#map=12/1.3332/103.7987'
Which produced this error:
OSError: cannot identify image file <_io.BytesIO object at 0x000001CC3CC4BC50>
First make sure that your GeoDataframe is in Web Mercator projection (epsg=3857). Once your Geodataframe is correctly georeferenced, you can achieve this by Geopandas reprojection:
df = df.to_crs(epsg=3857)
Once you have this done, you easily choose any of the supported map styles. A full list can be found in contextily.sources module, at the time of writing:
### Tile provider sources ###
ST_TONER = 'http://tile.stamen.com/toner/tileZ/tileX/tileY.png'
ST_TONER_HYBRID = 'http://tile.stamen.com/toner-hybrid/tileZ/tileX/tileY.png'
ST_TONER_LABELS = 'http://tile.stamen.com/toner-labels/tileZ/tileX/tileY.png'
ST_TONER_LINES = 'http://tile.stamen.com/toner-lines/tileZ/tileX/tileY.png'
ST_TONER_BACKGROUND = 'http://tile.stamen.com/toner-background/tileZ/tileX/tileY.png'
ST_TONER_LITE = 'http://tile.stamen.com/toner-lite/tileZ/tileX/tileY.png'
ST_TERRAIN = 'http://tile.stamen.com/terrain/tileZ/tileX/tileY.png'
ST_TERRAIN_LABELS = 'http://tile.stamen.com/terrain-labels/tileZ/tileX/tileY.png'
ST_TERRAIN_LINES = 'http://tile.stamen.com/terrain-lines/tileZ/tileX/tileY.png'
ST_TERRAIN_BACKGROUND = 'http://tile.stamen.com/terrain-background/tileZ/tileX/tileY.png'
ST_WATERCOLOR = 'http://tile.stamen.com/watercolor/tileZ/tileX/tileY.png'
# OpenStreetMap as an alternative
OSM_A = 'http://a.tile.openstreetmap.org/tileZ/tileX/tileY.png'
OSM_B = 'http://b.tile.openstreetmap.org/tileZ/tileX/tileY.png'
OSM_C = 'http://c.tile.openstreetmap.org/tileZ/tileX/tileY.png'
Keep in mind that you should not be adding actual x,y,z tile numbers in your tile URL (like you did in your "EDIT" example). ctx will take care of all this.
You can find a working copy-pastable example and further info at GeoPandas docs.
import contextily as ctx
# Dataframe you want to plot
gdf = GeoDataFrame(df, crs= {"init": "epsg:4326"}) # Create a georeferenced dataframe
gdf = gdf.to_crs(epsg=3857) # reproject it in Web mercator
ax = gdf.plot()
# choose any of the supported maps from ctx.sources
ctx.add_basemap(ax, url=ctx.sources.ST_TERRAIN)
ax.set_axis_off()
plt.show()
Contextily's default crs is epsg:3857. However, your data-frame is in different CRS. Use the following,refer the manual here:
ctx.add_basemap(ax, crs='epsg:4326', source=ctx.providers.Stamen.TonerLite)
Please, refer to this link for using different sources such as Stamen.Toner, Stamen.Terrain etc. (Stamen.Terrain is used as default).
Also, you can cast your data frame to EPSG:3857 by using df.to_crs(). In this case, you should skip crs argument inside ctx.add_basemap() function.
im too new to add a comment but I wanted to point out to those saying in the comments that they get a 404 error. Check you capitaliations, etc. Stamen's urls are specifc on this. For instance there is not an all caps call. It is only capitalize the first letter. For example:
ctx.add_basemap(ax=ax,url=ctx.providers.Stamen.Toner, zoom=10)
Sorry for disturbing.
I cannot render my map, I don't know why...
I read a csv file using ogr, which use a .vrt file I created, associated to the csv:
Then, I have a simple code to render my map, but I cannot it work: a map with an empty background is created, nothing on it ...
I get a warning, but I think it is normal:
Warning 1: The 'LON' and/or 'LAT' fields of the source layer are not declared as numeric fields,
so the spatial filter cannot be turned into an attribute filter on them
Do you have an idea ?
Thanks!
My .csv (called ZZZ.csv), just the begining and the interesting fields:
RecordID,VehId,DateTime,LAT,LON
0,2232,2012-04-07 18:54:39,32.801926,-116.871742
0,2232,2012-04-07 18:54:40,32.801888,-116.871727
0,2232,2012-04-07 18:54:41,32.801849,-116.871704
My .vrt:
<OGRVRTDataSource>
<OGRVRTLayer name="ZZZ">
<SrcDataSource>ZZZ.csv</SrcDataSource>
<GeometryType>wkbPoint</GeometryType>
<LayerSRS>WGS84</LayerSRS>
<GeometryField encoding="PointFromColumns" x="LON" y="LAT"/>
</OGRVRTLayer>
</OGRVRTDataSource>
My python module to render the card:
"""module mapniktest"""
import mapnik
#Defining the envelope
MIN_LAT = 30
MAX_LAT = +35
MIN_LON = -120
MAX_LON =-110
MAP_WIDTH = 1000
MAP_HEIGHT = 500
#defining the datasource: the .vrt above
datasource = mapnik.Ogr(file="ZZZ.vrt",layer = "ZZZ")
#Creating layer, rules and styles
layer = mapnik.Layer("ZZZ")
layer.datasource = datasource
layer.styles.append("LineStyle")
stroke = mapnik.Stroke()
stroke.color = mapnik.Color("#008000")
stroke.add_dash(50, 100)
symbol = mapnik.LineSymbolizer(stroke)
rule = mapnik.Rule()
rule.symbols.append(symbol)
style = mapnik.Style()
style.rules.append(rule)
print style
#creating the map
map = mapnik.Map(MAP_WIDTH, MAP_HEIGHT, "+proj=longlat +datum=WGS84")
map.append_style("LineStyle", style)
map.background = mapnik.Color("#8080a0")
map.layers.append(layer)
#displaying the map
map.zoom_to_box(mapnik.Envelope(MIN_LON, MIN_LAT, MAX_LON, MAX_LAT))
mapnik.render_to_file(map, "map.png")
thks!!!!
The problem is that you are applying a LineSymbolizer to point data. You need to either apply a PointSymbolizer or a MarkersSymbolizer to point data.
Also Mapnik 2.1 and above supports reading directly from CSV files so you do not need to use a VRT and the OGR plugin, although both should work similarly.