I've been working for a while with Choropleth and Cluster marker maps in Folium (which are great). My question is whether it is possible to combine them in one map, which is so that I can see how much one variable affects another. I can get both map types to work individually so no problems there. This is my attempted code to combine the two so far:
import pandas as pd
import folium
from folium.plugins import MarkerCluster
input_filename="input_filename.csv"
df = pd.read_csv(input_filename,encoding='utf8')
geo = 'blah.json'
comparison = 'comparison.csv'
comparison_data = pd.read_csv(comparison)
m = folium.Map(location=[Lat,Lon], zoom_start=12)
folium.Choropleth(
geo_data=geo,
name='choropleth',
data=comparison_data,
columns=['col1','col2'],
key_on='feature.properties.ID',
fill_color='OrRd',
fill_opacity=0.5,
line_opacity=0.5,
legend_name='Blah (%)'
).add_to(m)
folium.LayerControl().add_to(m)
marker_cluster = MarkerCluster().add_to(m)
for row in df.itertuples():
folium.Marker(location=[row.Lat,row.Lon],popup=row.Postcode).add_to(marker_cluster)
m
It produces the choropleth but won't layer the cluster markers as well. Worth noting that I've had a problem with cluster markers separately where they wouldn't display in Jupyter notebook, but I got around it by saving the file as an html, which was then viewable.
Ok so I've solved it, really pleased!! The solution was to do the marker cluster first, and then follow-up with the Choropleth:
import pandas as pd
import folium
from folium.plugins import MarkerCluster
m = folium.Map(location=[Lat,Lon], zoom_start=12)
input_filename="input_filename.csv"
df = pd.read_csv(input_filename,encoding='utf8')
geo = 'blah.json'
comparison = 'comparison.csv'
comparison_data = pd.read_csv(comparison)
folium.LayerControl().add_to(m)
marker_cluster = MarkerCluster().add_to(m)
for row in df.itertuples():
folium.Marker(location=[row.Lat,row.Lon],popup=row.Postcode).add_to(marker_cluster)
folium.Choropleth(
geo_data=geo,
name='choropleth',
data=comparison_data,
columns=['col1','col2'],
key_on='feature.properties.ID',
fill_color='OrRd',
fill_opacity=0.5,
line_opacity=0.5,
legend_name='Blah (%)'
).add_to(m)
m
from random import randint
import folium
def rgb_to_hex(rgb):
return '#%02x%02x%02x' % rgb
mp = folium.Map(location=[40.6, -73.7], scale = 10)
colors = []
while len(colors) != 50:
r = randint(0, 255)
g = randint(0, 255)
b = randint(0, 255)
if rgb_to_hex((r, g, b)) not in colors:
colors.append(rgb_to_hex((r, g, b)))
for j in range(df.shape[0]):
lat = df.iloc[j]['latitude']
lon = df.iloc[j]['longitude']
color = colors[int(df.iloc[j]['clust'])]
folium.Circle(location=[lat, lon], radius=8, color = color).add_to(mp)
Related
I have the following script. However, I'm getting an error that points are the data I'm passing to the location parameter. When printing this, I'm getting what location generally receives.
Here is an example of the txt info: VOLCANX020,NUMBER,NAME,LOCATION,STATUS,ELEV,TYPE,TIMEFRAME,LAT,LON
509.000000000000000,1201-01=,Baker,US-Washington,Historical,3285.000000000000000,Stratovolcanoes,D3,48.7767982,-121.8109970
import folium
import pandas as pd
map = folium.Map(location=[38.58, -99.09], zoom_start=4, tiles = "Stamen Terrain")
# Reads the data
df_volcanos = pd.read_csv('Volcanoes.txt')
fg_volcanoes=folium.FeatureGroup(name="Volcanos Map")
fg_population=folium.FeatureGroup(name='Populations')
for index, data in df_volcanos.head(1).iterrows():
fg_volcanoes.add_child(folium.CircleMarker(location=data[['LAT','LON']].values,
radius=6,
popup=f"{data['ELEV']} mts",
fill_color='green' if data['ELEV'] < 1000 else 'orange' if data['ELEV'] < 3000 else 'red',
color="grey",
fill_opacity=1))
map.add_child(folium.LayerControl())
map.save('Map2.html')
My modification is to make the circle marker itself belong to a group and add that group to the map. Also, in the sequential processing of the data frame, I did not use indexes to specify rows, so I used .loc to limit the rows. At the same time, I also modified the row specification in the popup.
import folium
m = folium.Map(location=[38.58, -99.09], zoom_start=4, tiles="Stamen Terrain")
fg_volcanoes=folium.FeatureGroup(name="Volcanos Map")
fg_population=folium.FeatureGroup(name='Populations')
for index, data in df_volcanos.head(1).iterrows():
color = 'green' if df_volcanos.loc[index,'ELEV'] < 1000 else 'orange' if df_volcanos.loc[index, 'ELEV'] > 3000 else 'red'
#fg_volcanoes.add_child(
folium.CircleMarker(
location=df_volcanos[['LAT','LON']].values[0].tolist(),
radius=6,
popup=f"{df_volcanos.loc[index,'ELEV']} mts",
fill_color=color,
color="grey",
fill_opacity=1
).add_to(fg_volcanoes)
#)
fg_volcanoes.add_to(m)
m.add_child(folium.LayerControl())
#m.save('Map2.html')
m
Hi I am trying to extract data from a netCDF file, but the data is upside down. How can I reverse the database:
The data I want to extract is the height data from the (netcdf) at the points I have in the CSV file. my Data:
import numpy as np
from netCDF4 import Dataset
import matplotlib.pyplot as plt
import pandas as pd
from mpl_toolkits.basemap import Basemap
from matplotlib.patches import Path, PathPatch
csv_data = np.loadtxt('CSV with target coordinates',skiprows=1,delimiter=',')
num_el = csv_data[:,0]
lat = csv_data[:,1]
lon = csv_data[:,2]
value = csv_data[:,3]
data = Dataset("elevation Data",'r')
lon_range = data.variables['x_range'][:]
lat_range = data.variables['y_range'][:]
topo_range = data.variables['z_range'][:]
spacing = data.variables['spacing'][:]
dimension = data.variables['dimension'][:]
z = data.variables['z'][:]
lon_num = dimension[0]
lat_num = dimension[1]
etopo_lon = np.linspace(lon_range[0],lon_range[1],dimension[0])
etopo_lat = np.linspace(lat_range[0],lat_range[1],dimension[1])
topo = np.reshape(z, (lat_num, lon_num))
height = np.empty_like(num_el)
desired_lat_idx = np.empty_like(num_el)
desired_lon_idx = np.empty_like(num_el)
for i in range(len(num_el)):
tmp_lat = np.abs(etopo_lat - lat[i]).argmin()
tmp_lon = np.abs(etopo_lon - lon[i]).argmin()
desired_lat_idx[i] = tmp_lat
desired_lon_idx[i] = tmp_lon
height[i] = topo[tmp_lat,tmp_lon]
height[height<-10]=0
print(len(desired_lat_idx))
print(len(desired_lon_idx))
print(len(height))
dfl= pd.DataFrame({
'Latitude' : lat.reshape(-1),
'Longitude': lon.reshape(-1),
'Altitude': height.reshape(-1)
});
print(dfl)
# but the Lat should not be changed here (the dfl must be correct)
df =dfl
lat=np.array(df['Latitude'])
lon=np.array(df['Longitude'])
val=np.array(df['Altitude'])
m = basemap.Basemap(projection='robin', lon_0=0, lat_0=0, resolution='l',area_thresh=1000)
m.drawcoastlines(color = 'black')
x,y = m(lon,lat)
colormesh= m.contourf(x,y,val,100, tri=True, cmap = 'terrain')
plt.colorbar(location='bottom',pad=0.04,fraction=0.06)
plt.show()
I have already tried:
lat = csv_data[:,1]
lat= lat*(-1)
But this didnĀ“t work
It's a plotting artifact().
Just do:
colormesh= m.contourf(x,y[::-1],val,100, tri=True, cmap = 'terrain')
y[::-1] will reverse the order of the y latitude elements (as opposed to the land-mass outlines; and while keeping the x longitude coordinates the same) and hence flip them.
I've often had this problem with plotting numpy image data in the past.
Your raw CSV data are unlikely to be flipped themselves (why would they be?). You should try sanity-checking them [I am not a domain expert I'm afraid]! Overlaying an actual coordinate grid can help with this.
Another way to do it is given here: Reverse Y-Axis in PyPlot
You could also therefore just do
ax = plt.gca()
ax.invert_yaxis()
I have a DataFrame as below, I want to convert data to a multi polygon DataFrame, because I want to plot each multi polygon on a map.
I know how to convert if I have two data point, but with 6 data point, I don't know how to convert it. can anyone help me please.
geometry = [Point(xy) for xy in zip(neightrip_counts_.lan0, neightrip_counts_.long0)]
geometry
#neightrip_counts_.lan1, neightrip_counts_.long1,neightrip_counts_.lan2, neightrip_counts_.long2
lan0 long0 lan1 long1 lan2 long2
0 59.915667 10.777567 59.916738 10.779916 59.914943 10.773977
1 59.929853 10.711515 59.929435 10.713682 59.927596 10.710033
2 59.939230 10.759170 59.937205 10.760581 59.943750 10.760306
3 59.912520 10.762240 59.911594 10.761774 59.912347 10.763815
4 59.929634 10.732839 59.927140 10.730981 59.931081 10.736003
Let me rename the dataframe neightrip_counts_ as df for brevity. Here is the relevant code that will create a polygon for each row of dataframe.
df['geometry'] = [Polygon([(z[0],z[1]), (z[2],z[3]), (z[4],z[5])]) for z in zip(df.long0, df.lan0, df.long1, df.lan1, df.long2, df.lan2)]
gpdf = df.set_geometry("geometry", drop=True)
gpdf.plot()
By the way, you must be careful about the sequence of (long, lat).
start_coords = [ gdf.centroid[0].x, gdf.centroid[0].y] # is wrong
Use this in stead.
start_coords = [ gdf.centroid[0].y, gdf.centroid[0].x]
Edit
For the benefits of the readers, here is the complete runnable code:
import pandas as pd
import geopandas as gpd
from io import StringIO
from shapely.geometry import Polygon, Point, LineString
import numpy as np
import folium
data1 = """index lan0 long0 lan1 long1 lan2 long2
0 59.915667 10.777567 59.916738 10.779916 59.914943 10.773977
1 59.929853 10.711515 59.929435 10.713682 59.927596 10.710033
2 59.939230 10.759170 59.937205 10.760581 59.943750 10.760306
3 59.912520 10.762240 59.911594 10.761774 59.912347 10.763815
4 59.929634 10.732839 59.927140 10.730981 59.931081 10.736003"""
# read/parse data into dataframe
df0 = pd.read_csv(StringIO(data1), sep='\s+', index_col='index')
# create `geometry` column
df0['geometry'] = [Polygon([(xy[0],xy[1]), (xy[2],xy[3]), (xy[4],xy[5])]) \
for xy in zip(df0.long0, df0.lan0, df0.long1, df0.lan1, df0.long2, df0.lan2)]
# set geometry
gpdf = df0.set_geometry("geometry", drop=True)
# do check plot. (uncomment next line)
#gpdf.plot()
# make geojson
center_pt = gpdf.centroid[0].y, gpdf.centroid[0].x
gdf_json = gpdf.to_json()
# plot the geojson on the folium webmap
webmap = folium.Map(location = center_pt, zoom_start = 13, min_zoom = 3)
folium.GeoJson(gdf_json, name='data_layer_1').add_to(webmap)
# this opens the webmap
webmap
Output screen capture (of interactive webmap):
Try this, assuming the 'lan' is latitude.
import geopandas as gpd
from shapely.geometry import Polygon
import numpy as np
import pandas as pd
import folium
# ....
def addpolygeom(row):
row_array = np.array(row)
# split dataframe row to a list of tuples (lat, lon)
coords = [tuple(i)[::-1] for i in np.split(row_array, range(2, row_array.shape[0], 2))]
polygon = Polygon(coords)
return polygon
# Convert points to shapely geometry
neightrip_counts_['geometry'] = neightrip_counts_.apply(lambda x: addpolygeom(x), axis=1)
# Create a GeoDataFrame
gdf = gpd.GeoDataFrame(neightrip_counts_, geometry='geometry')
start_coords = [ gdf.centroid[0].y, gdf.centroid[0].x]
gdf_json = gdf.to_json()
map = folium.Map(start_coords, zoom_start=4)
folium.GeoJson(gdf_json, name='mypolygons').add_to(map)
I have been trying to apply SOM on my dataframe, my dataframe has 25 columns where each column represents a house, each house has a values for power consumption for two years, and I want to cluster the data with number of clusters = 3.
I have done the following:
import sys
sys.path.insert(0, '../')
%load_ext autoreload
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pylab import plot,axis,show,pcolor,colorbar,bone
from matplotlib.patches import Patch
%matplotlib inline
from minisom import MiniSom
from sklearn.preprocessing import minmax_scale, scale
%autoreload 2
data1 = pd.read_excel(r"C:\Users\user\Desktop\Thesis\Tarek\Consumption.xlsx")
data1['h1'] = data1['h1'].str.split(';').str[2].astype('float')
data1['h2'] = data1['h2'].str.split(';').str[2].astype('float')
data1['h3'] = data1['h3'].str.split(';').str[2].astype('float')
data1['h4'] = data1['h4'].str.split(';').str[2].astype('float')
data1['h5'] = data1['h5'].str.split(';').str[2].astype('float')
data1['h6'] = data1['h6'].str.split(';').str[2].astype('float')
data1['h7'] = data1['h7'].str.split(';').str[2].astype('float')
data1['h8'] = data1['h8'].str.split(';').str[2].astype('float')
data1['h9'] = data1['h9'].str.split(';').str[2].astype('float')
data1['h10'] = data1['h10'].str.split(';').str[2].astype('float')
data1['h11'] = data1['h11'].str.split(';').str[2].astype('float')
data1['h12'] = data1['h12'].str.split(';').str[2].astype('float')
data1['h13'] = data1['h13'].str.split(';').str[2].astype('float')
data1['h14'] = data1['h14'].str.split(';').str[2].astype('float')
data1['h15'] = data1['h15'].str.split(';').str[2].astype('float')
data1['h16'] = data1['h16'].str.split(';').str[2].astype('float')
data1['h17'] = data1['h17'].str.split(';').str[2].astype('float')
data1['h18'] = data1['h18'].str.split(';').str[2].astype('float')
data1['h19'] = data1['h19'].str.split(';').str[2].astype('float')
data1['h20'] = data1['h20'].str.split(';').str[2].astype('float')
data1['h21'] = data1['h21'].str.split(';').str[2].astype('float')
data1['h22'] = data1['h22'].str.split(';').str[2].astype('float')
data1['h23'] = data1['h23'].str.split(';').str[2].astype('float')
data1['h24'] = data1['h24'].str.split(';').str[2].astype('float')
data1['h25'] = data1['h25'].str.split(';').str[2].astype('float')
data1.fillna(0,inplace=True)
data1=data1.round(decimals=2)
X=data1.values
som =MiniSom(x=3,y=3,input_len=25,sigma=1.0, learning_rate=0.5)
som.random_weights_init(X)
som.train_batch(data=X ,num_iteration=1000,verbose=True)
bone()
pcolor(som.distance_map().T)
colorbar()
markers = ['o' , 's','v']
colors = ['r', 'g','y']
for i, x in enumerate(X):
w = som.winner(x)
plot(w[0] + 0.5,
w[1] + 0.5,
markers[i],
markeredgecolor = colors[i],
markerfacecolor = 'None',
markersize = 10,
markeredgewidth = 2)
show()
when I am running the code, I am getting this error:
IndexError: list index out of range
please any tips to add the markers and colors in the right way without having any problems, and I would be glad if any one can help, I am a bit new to Python and tried to find a solution but I couldn`t find any.
The problem seems to be that the length of your X=data1.values is around 25 but the length of your markers and colors is only 3. So in the following for loop, when i is 3, you are trying to access markers[3] and colors[3] which throws an IndexError because both markers and colors goes up to index 2 (indexing starts from 0 in python)
for i, x in enumerate(X):
One solution is to define custom list of 25 markers and 25 colors. While you might want to define your own markers, you can leave the colors out and let the code choose automatic colors for the markeredgecolor
I am having a pandas DataFrames with latitude longitude of start and end of a route, so these are my columns ('origin_lat, 'origin_lon',destination_lat','destination_lon').
I am able to plot the locations on Folium map but I am looking for a way to plot the routes between each location.
Here is the code I am using:
m = folium.Map([16.7, 81.095], zoom_start=11)
m
# mark each origin as a point
for index, row in df.iterrows():
folium.CircleMarker([row['origin_lat'], row['origin_lng']],
radius=15,
fill_color="#3db7e4", # divvy color
).add_to(m)
for index, row in df.iterrows():
folium.CircleMarker([row['destination_lat'], row['destination_lng']],
radius=15,
fill_color="red", # divvy color
).add_to(m)
#add routes
folium.PolyLine([list(zip(df.origin_lat, df.origin_lng)),list(zip(df.destination_lat, df.destination_lng))], line_opacity = 0.5, color='white',line_weight=5).add_to(m)
The code I am using is connecting all the origin locations together and all the destination locations together but I want to plot routes between origin and destination locations instead. Any way I can fix it?
If I understood your question correctly, I believe I have your solution
Some imports
import pandas as pd
import numpy as np
import folium
Some sample data
centroid_lat = 16.7
centroid_lon = 81.095
x = .1
n = 10
o_lats = np.random.uniform(low=centroid_lat - x, high=centroid_lat + x, size=(n,))
o_lons = np.random.uniform(low=centroid_lon - x, high=centroid_lon + x, size=(n,))
d_lats = np.random.uniform(low=centroid_lat - x, high=centroid_lat + x, size=(n,))
d_lons = np.random.uniform(low=centroid_lon - x, high=centroid_lon + x, size=(n,))
df = pd.DataFrame({'origin_lng' : o_lons, 'origin_lat' : o_lats,
'destination_lng': d_lons, 'destination_lat': d_lats})
print(df.head())
destination_lat destination_lng origin_lat origin_lng
0 16.797057 81.074000 16.660164 81.080038
1 16.615371 81.001004 16.772645 80.997770
2 16.784289 81.117082 16.670008 81.032719
3 16.686201 81.184775 16.787999 81.189585
4 16.757704 81.127280 16.720080 81.178466
Then the map. No need for two for loops and I'm creating a line each iteration
m = folium.Map([centroid_lat, centroid_lon], zoom_start=11)
for _, row in df.iterrows():
folium.CircleMarker([row['origin_lat'], row['origin_lng']],
radius=15,
fill_color="#3db7e4", # divvy color
).add_to(m)
folium.CircleMarker([row['destination_lat'], row['destination_lng']],
radius=15,
fill_color="red", # divvy color
).add_to(m)
folium.PolyLine([[row['origin_lat'], row['origin_lng']],
[row['destination_lat'], row['destination_lng']]]).add_to(m)
m