def tiles_for_trail(trail_code, trail_section, alternates, trail_buffer, town_buffer, min_zoom, max_zoom, tms): """Get map tile coordinates for trail """ # Load geometries trail = Trail(trail_code=trail_code) track = trail.track(trail_section=trail_section, alternates=alternates) towns = trail.towns(trail_section=trail_section) # Create buffers if trail_buffer > 0: track.geometry = geom.buffer(track, distance=trail_buffer, unit='mile') if town_buffer > 0: towns.geometry = geom.buffer(towns, distance=town_buffer, unit='mile') # Combine into single polygon gdf = gpd.GeoDataFrame(pd.concat([track, towns], sort=False, axis=0)) polygon = gdf.unary_union # Find tiles scheme = 'tms' if tms else 'xyz' zoom_levels = range(min_zoom, max_zoom + 1) tiles = tiles_for_polygon(polygon, zoom_levels=zoom_levels, scheme=scheme) # Coerce to strings like # [x, y, z] s = '\n'.join([f'[{t[0]}, {t[1]}, {t[2]}]' for t in tiles]) click.echo(s)
def get_campsites_near_trail(self, trail): section_name, trail = next(Halfmile().trail_iter()) trail_buf = geom.buffer(trail, distance=2, unit='mile') trail_buf = gpd.GeoDataFrame(geometry=trail_buf) buf = geom.buffer(trail, distance=2, unit='mile').unary_union local_path = self.raw_dir / 'RIDBFullExport_V1_CSV.zip' z = ZipFile(local_path) z.namelist() df = pd.read_csv(BytesIO(z.read('Facilities_API_v1.csv'))) gdf = gpd.GeoDataFrame( df, geometry=df.apply(lambda row: Point(row['FacilityLongitude'], row[ 'FacilityLatitude']), axis=1))
def download(self, trail: gpd.GeoDataFrame, buffer_dist=None, buffer_unit='mile', overwrite=False): """Download polygon shapefile and intersect with PCT track Args: - trail: gdf of trail to use to find polygons that intersect - buffer_dist: distance to use for trail buffer when intersecting with polygons. By default is None, so no buffer will be used. - buffer_unit: unit to use for buffer - overwrite: whether to overwrite existing data """ assert self.save_dir is not None, 'self.save_dir must be set' assert self.url is not None, 'self.url must be set' assert self.filename is not None, 'self.filename must be set' # Cache original download in self.raw_dir parsed_url = urlparse(self.url) raw_fname = Path(parsed_url.path).name raw_path = self.raw_dir / raw_fname if overwrite or (not raw_path.exists()): urlretrieve(self.url, raw_path) # Now load the saved file as a GeoDataFrame with open(raw_path, 'rb') as f: with fiona.BytesCollection(f.read()) as fcol: crs = fcol.crs gdf = gpd.GeoDataFrame.from_features(fcol, crs=crs) # Reproject to WGS84 gdf = gdf.to_crs(epsg=4326) # Use provided `trail` object trail = trail.to_crs(epsg=4326) # Intersect with the trail if buffer_dist is not None: buf = geom.buffer(trail, distance=buffer_dist, unit=buffer_unit) # Returned as GeoSeries; coerce to GDF if not isinstance(buf, gpd.GeoDataFrame): buf = gpd.GeoDataFrame(geometry=buf) buf = buf.to_crs(epsg=4326) intersection = sjoin(gdf, buf, how='inner') else: intersection = sjoin(gdf, trail, how='inner') # Make sure I have valid geometries intersection = geom.validate_geom_gdf(intersection) # Do any specific steps, to be overloaded in subclasses intersection = self._post_download(intersection) # Save to GeoJSON self.save_dir.mkdir(exist_ok=True, parents=True) intersection.to_file(self.save_dir / self.filename, driver='GeoJSON')
def _create_buffer(self, distance: float = 20): """Create buffer around USFS pct track Args: distance: buffer radius in miles """ trail = self.trail() buffer = geom.buffer(trail, distance=20, unit='mile') save_dir = self.data_dir / 'pct' / 'polygon' / 'usfs' save_dir.mkdir(parents=True, exist_ok=True) buffer.to_file(save_dir / f'buffer{distance}mi.geojson', driver='GeoJSON')
def handle_sections(self, use_cache: bool = True): sections = VALID_TRAIL_SECTIONS[self.trail_code] for section_name in sections: hm_sections = TRAIL_HM_XW[section_name] track = self.hm.trail_section(hm_sections, alternates=True) wpt = self.hm.wpt_section(hm_sections) buf = geom.buffer(track, distance=2, unit='mile').unary_union # section = # track self = TrailSection(buffer=buf, section_name=section_name, use_cache=use_cache) self.main(wpt=wpt)
def get_osm_network(self, buffer_dist, buffer_unit, use_cache=True): """Use osmnx to get network of roads/trails around trail geometry """ # Take buffer of approximate trail approx_trail_buffer_gdf = geom.buffer(self.approx_trail_gdf, distance=buffer_dist, unit=buffer_unit, crs=self.crs) # Consolidate GeoDataFrame to shapely geometry approx_trail_buffer = approx_trail_buffer_gdf.unary_union # Get graph G = self.osm.get_ways_for_polygon(polygon=approx_trail_buffer, section_name=self.trail_section, source='geofabrik', use_cache=use_cache) # Get way ids that are part of the trail trail_way_ids = self._get_osm_way_ids_for_trail() trail_way_ids = list(map(int, trail_way_ids)) # Set attribute on each edge and node if it's part of the trail # trail_nodes is trail_nodes = set() trail_edges = [] for u, v, k, way_id in G.edges(keys=True, data='osmid'): if way_id not in trail_way_ids: continue trail_nodes.add(u) trail_nodes.add(v) trail_edges.append((u, v, k)) # Add _trail=True for these nodes and edges nx.set_node_attributes(G, name='_trail', values={k: True for k in trail_nodes}) nx.set_edge_attributes(G, name='_trail', values={(u, v, k): True for u, v, k in trail_edges}) return G
def get_tile_indices(gdf, buffer_dists, max_zoom): """Generate nested tile indices For a given GeoDataFrame, generate tile coordinates for each buffer distance. These tile coordinates should include include the difference between the current buffer distance and the smaller buffer distance, so that the tile coordinates can nest. """ # Generate buffers around geometry and generate tile indices for that # geometry tile_indices = {} for buffer_dist in buffer_dists: if buffer_dist > 0: buf = buffer(gdf, distance=buffer_dist, unit='mile').unary_union else: buf = gdf.unary_union tiles = tiles_for_polygon(buf, zoom_levels=range(0, max(max_zoom) + 1)) tile_indices[buffer_dist] = tiles # When multiple buffer distances are provided, change the higher values to # be the difference in tiles between it and the next lowest buffer. So if # the buffer distances provided are [2, 5, 10], then change tile_indices[5] # to contain only the tile coordinates not in tile_indices[2] # # NOTE: make sure you create a new dict, otherwise, if you start at the # bottom, when you're comparing buffer 5 and buffer 10, you might # unintentionally take the set difference of 10 and (5 diff 2), which would # include the original 2-buffer tiles. Either start at the largest dist, or # you have to diff every lower value, or create a new dict... buffer_dists = sorted(buffer_dists) if len(buffer_dists) > 1: tile_indices_new = {} tile_indices_new[min(buffer_dists)] = tile_indices[min(buffer_dists)] for prev_dist, dist in zip(buffer_dists, buffer_dists[1:]): tile_indices_new[dist] = set(tile_indices[dist]).difference( tile_indices[prev_dist]) tile_indices = tile_indices_new # tile_indices now has the minimum tile coordinates for each zoom level return tile_indices
def _download_nhd_for_line(self, line: Union[LineString, GDF], overwrite): """Download National Hydrography Dataset for trail Downloads NHD files within 2 miles of trail """ if not isinstance(line, gpd.GeoDataFrame): line = gpd.GeoDataFrame([], lineetry=[line]) line.crs = {'init': 'epsg:4326'} buf = geom.buffer(line, distance=2, unit='mile').unary_union gdfs = self._get_HU8_units_for_geometry(buf) hu8_ids = gdfs['HUC8'].unique() baseurl = 'https://prd-tnm.s3.amazonaws.com/StagedProducts/Hydrography/' baseurl += 'NHD/HU8/HighResolution/GDB/' for hu8_id in hu8_ids: name = f'NHD_H_{hu8_id}_HU8_GDB.zip' url = baseurl + name path = self.raw_dir / name if overwrite or (not path.exists()): urlretrieve(url, path)
def transit(self, trail=True, town=True, trail_buffer_dist=1000, trail_buffer_unit='meter'): """Get transit information for trail """ transit = data_source.Transit() # Get all stops that intersect trail and town geometries all_all_stops = {} all_nearby_stops = {} all_routes = {} if trail: for section_name, gdf in self.hm.trail_iter(): trail_buf = geom.buffer(gdf, distance=trail_buffer_dist, unit=trail_buffer_unit).unary_union _nearby_stops, _all_stops, _routes = transit.download( trail_buf) # Add each dict to `all_${dict}`, but set the _trail key to True for key, val in _nearby_stops.items(): all_nearby_stops[key] = all_nearby_stops.get(key, val) all_nearby_stops[key]['_trail'] = True for key, val in _all_stops.items(): all_all_stops[key] = all_all_stops.get(key, val) all_all_stops[key]['_trail'] = True for key, val in _routes.items(): all_routes[key] = all_routes.get(key, val) all_routes[key]['_trail'] = True if town: for polygon in self.towns().geometry: _nearby_stops, _all_stops, _routes = transit.download(polygon) # Add each dict to `all_${dict}`, but set the _trail key to True for key, val in _nearby_stops.items(): all_nearby_stops[key] = all_nearby_stops.get(key, val) all_nearby_stops[key]['_town'] = True for key, val in _all_stops.items(): all_all_stops[key] = all_all_stops.get(key, val) all_all_stops[key]['_town'] = True for key, val in _routes.items(): all_routes[key] = all_routes.get(key, val) all_routes[key]['_town'] = True # Combine all_all_stops and all_nearby_stops into single dict stops = {} for key, val in all_nearby_stops.items(): stops[key] = stops.get(key, val) stops[key]['_nearby_stop'] = True for key, val in all_all_stops.items(): stops[key] = stops.get(key, val) stops_features = [] for key, val in stops.items(): props = {k: v for k, v in val.items() if k != 'geometry'} f = geojson.Feature(id=key, geometry=val['geometry'], properties=props) stops_features.append(f) routes_features = [] for key, val in all_routes.items(): props = {k: v for k, v in val.items() if k != 'geometry'} f = geojson.Feature(id=key, geometry=val['geometry'], properties=props) routes_features.append(f) stops_fc = geojson.FeatureCollection(stops_features) routes_fc = geojson.FeatureCollection(routes_features) return stops_fc, routes_fc
def wikipedia_articles(self, buffer_dist=2, buffer_unit='mile', attrs=['title', 'url']): """Get wikipedia articles for trail Args: - buffer_dist: numerical distance for buffer around trail - buffer_unit: units for buffer_dist, can be 'mile', 'meter', 'kilometer' - attrs: list of wikipedia page attributes to keep. Geometry is always kept. Options are: - categories: List of categories of a page. I.e. names of subsections within article - content: Plain text content of the page, excluding images, tables, and other data. - html: Get full page HTML. Warning: this can be slow for large pages. - best_image: My attempt to get the single best image url. - images: List of URLs of images on the page. - links: List of titles of Wikipedia page links on a page. - original_title: - pageid: - parent_id: Revision ID of the parent version of the current revision of this page. See revision_id for more information. - references: List of URLs of external links on a page. May include external links within page that aren’t technically cited anywhere. - revision_id: Revision ID of the page. The revision ID is a number that uniquely identifies the current version of the page. It can be used to create the permalink or for other direct API calls. See Help:Page history for more information. - sections: List of section titles from the table of contents on the page. - summary: Plain text summary of the page. - title: Title of the page - url: URL of the page """ # is best_image asked for best_image = 'best_image' in attrs # Make sure it's not left in attrs list attrs = [attr for attr in attrs if attr != 'best_image'] # Make sure desired attributes are valid valid_attrs = [ 'categories', 'content', 'html', 'images', 'links', 'original_title', 'pageid', 'parent_id', 'references', 'revision_id', 'sections', 'summary', 'title', 'url' ] assert (all(attr) in valid_attrs for attr in attrs), 'Invalid attrs' # Get trail track as a single geometric line trail = self.hm.trail_full(alternates=False) buf = geom.buffer(trail, distance=buffer_dist, unit=buffer_unit).unary_union wiki = data_source.Wikipedia() pages = wiki.find_pages_for_polygon(buf) data = [] for page in pages: d = {} if best_image: d['best_image'] = wiki.best_image_on_page(page) for attr in attrs: d[attr] = getattr(page, attr) # Page coordinates are in lat, lon order d['geometry'] = Point(page.coordinates[::-1]) data.append(d) gdf = gpd.GeoDataFrame(data, crs={'init': 'epsg:4326'}) return gdf
def buffer_iter(self, distance, unit='mile', alternates=True): """Get buffer around each section """ for section_name, gdf in self.trail_iter(alternates=alternates): buf = geom.buffer(gdf, distance=distance, unit=unit).unary_union yield section_name, buf
def buffer_full(self, distance, unit='mile', alternates=True): """ """ trail = self.trail_full(alternates=alternates) buf = geom.buffer(trail, distance=distance, unit=unit).unary_union return buf