"""
This file contains functions for making maps
"""
from .spatial import *
from .report import *
# import cartopy.crs as _ccrs
import matplotlib.pyplot as _plt
#######################################################################################################################
[docs]def field_explorer(artifacts, points, fields_shp_path, html_file_out=''):
"""Create bokeh map with summary information about all fields
Parameters
----------
artifacts, points : pandas DataFrames
fields_shp_path : str
Path to the shapefile of fields
html_file_out : str
Path to save the output file
Returns
-------
None
Notes
-----
If you want to print the output in a Jupyter notebook, use
`from bokeh.io import output_notebook; output_notebook()`
before running the function.
"""
from bokeh.io import show
from bokeh.plotting import figure, output_file
from bokeh.models import (GeoJSONDataSource,
HoverTool, PanTool, WheelZoomTool, BoxZoomTool, ResetTool,
NumeralTickFormatter)
geo_artifacts = find_geo_field(artifacts, fields_shp_path) # find geofield for artifacts
geo_points = find_geo_field(points, fields_shp_path) # find geofield for points
fields_sum = fields_summary_table(geo_points, geo_artifacts) # summarize artifacts by geofield
fields_shp = read_fields_shp(fields_shp_path) # get fields shapefile as geodataframe
# attach artifact summaries to fields geodataframe
fields_merge = fields_shp.merge(fields_sum, how='left', left_on='fid', right_on='Id')
fields_merge = fields_merge.rename(columns={ # renaming columns makes them usable
'Polígono': 'polygon', # as tooltips in the bokeh plot
'Parcela': 'parcel',
'Subparcela': 'subparcel',
'Id': 'id',
'Núm. Pts.': 'n_pts',
'Núm. Frags.': 'n_frags',
'Pos. Pts.': 'pos_pts'
})
surveyed = fields_merge[~fields_merge['n_pts'].isna()] # get separate dataframes for
unsurveyed = fields_merge[fields_merge['n_pts'].isna()] # surveyed and unsurveyed fields
surveyed_geojson = surveyed.to_json() # convert both to geo_json
unsurveyed_geojson = unsurveyed.to_json()
surveyed_source = GeoJSONDataSource(geojson=surveyed_geojson) # convert JSON to
unsurveyed_source = GeoJSONDataSource(geojson=unsurveyed_geojson) # bokeh GeoJSONDataSource
output_file(html_file_out)
surveyed_hover = [
('Status', 'surveyed'),
('Campo', '@fid'),
('Núm. Pts.', '@n_pts'),
('Pos. Pts.', '@pos_pts'),
('Núm. Frags.', '@n_frags')
]
unsurveyed_hover = [
('Status', 'not surveyed'),
('Campo', '@fid')
]
p = figure(plot_width=600, plot_height=450,
tools=[PanTool(), WheelZoomTool(), BoxZoomTool(), ResetTool()])
p.xaxis[0].formatter = NumeralTickFormatter(format="0")
p.yaxis[0].formatter = NumeralTickFormatter(format="0")
s = p.patches(xs='xs', ys='ys', source=surveyed_source, line_color='gray', fill_color='lightgreen')
p.add_tools(HoverTool(renderers=[s], tooltips=surveyed_hover))
u = p.patches(xs='xs', ys='ys', source=unsurveyed_source, line_color='gray', fill_color='seashell')
p.add_tools(HoverTool(renderers=[u], tooltips=unsurveyed_hover))
show(p)
#######################################################################################################################
[docs]def single_field_map(field, fields_gdf, axis_len=500, save_path=None):
"""Make a map centered on a specified field
Parameters
----------
field : str
Survey field ID for field of interest
fields_gdf : geopandas GeoDataFrame
geopandas GeoDataFrame of all fields
axis_len : int, optional
Length of both x and y axes
save_path : str, optional
If not None, location and filename for output
Returns
-------
field_map : matplotlib Figure
Styled map of desired field
"""
selected = fields_gdf[fields_gdf['fid'] == field] # isolate desired field as a gdf
unselected = fields_gdf[fields_gdf['fid'] != field] # get all other fields as a gdf
bounds = selected.bounds # find bounding box of selected field and extract mins and maxs
minx, maxx, miny, maxy = bounds.minx.iloc[0], bounds.maxx.iloc[0], bounds.miny.iloc[0], bounds.maxy.iloc[0]
h_mid = minx + ((maxx - minx) / 2) # find horizontal midpoint of selected field's bounding box
v_mid = miny + ((maxy - miny) / 2) # find vertical midpoint
xax_min = h_mid - axis_len / 2 # find min/max dimensions from midpoints
xax_max = h_mid + axis_len / 2
yax_min = v_mid - axis_len / 2
yax_max = v_mid + axis_len / 2
field_map = draw_single_field_map(selected, unselected, xlim=(xax_min, xax_max), ylim=(yax_min, yax_max))
if save_path: # save, if desired
field_map.savefig(save_path)
_plt.close('all')
return field_map
#######################################################################################################################
# def draw_single_field_map(target_field_gdf, other_fields_gdf, xlim, ylim):
# """Create map image for single field
#
# Parameters
# ----------
# target_field_gdf : geopandas GeoDataFrame
# A GeoDataFrame of one row with the survey field of interest
# other_fields_gdf : geopandas GeoDataFrame
# All other fields
# xlim : two-member list or tuple of floats
# x axis min and max
# ylim : two-member list or tuple of floats
# y axis min and max
#
# Returns
# -------
# fig : matplotlib Figure
# Figure object that can be printed or saved
#
# Notes
# -----
# This function is defined separately from `single_field_map()` so that all of the cartographic/design elements of the plot are conveniently grouped in one function
#
# """
#
# fig = _plt.figure(figsize=(5, 5)) # create empty figure
# ax = fig.add_subplot(1, 1, 1, projection=_ccrs.UTM('31S')) # create ax to plot on
# ax.set_extent((xlim[0], xlim[1], ylim[0], ylim[1]), crs=_ccrs.UTM('31S'))
# ax.add_geometries(other_fields_gdf.geometry, crs=_ccrs.UTM('31S'), facecolor='gray') # add 'other' fields
# ax.add_geometries(target_field_gdf.geometry, crs=_ccrs.UTM('31S'), facecolor='green') # add central field
#
# return fig
#######################################################################################################################