def digestNodesFromSHP(sourcePath): 'Import nodes from a shapefile' # Initialize shapeData = osgeo.ogr.Open(sourcePath) layer = shapeData.GetLayer() # Prepare spatial reference proj4 = layer.GetSpatialRef().ExportToProj4() # Prepare nodePacks nodePacks = [] for featureIndex in xrange(layer.GetFeatureCount()): # Get feature feature = layer.GetFeature(featureIndex) geometry = feature.GetGeometryRef() # Build nodePack valueByLabel = feature.items() nodePack = dict((label.lower(), value) for label, value in valueByLabel.iteritems() if value not in ['', None]) nodePack['x'] = geometry.GetX() nodePack['y'] = geometry.GetY() # Append nodePacks.append(nodePack) # Return return proj4, nodePacks
def _determine_valid_viewpoints(dem_path, structures_path): """Determine which viewpoints are valid and return them. A point is considered valid when it meets all of these conditions: 1. The point must be within the bounding box of the DEM 2. The point must not overlap a DEM pixel that is nodata 3. The point must not have the same coordinates as another point All invalid points are skipped, and a logger message is written for the feature. Args: dem_path (str): The path to a GDAL-compatible digital elevation model raster on disk. The projection must match the projection of the ``structures_path`` vector. structures_path (str): The path to a GDAL-compatible vector containing point geometries and, optionally, a few fields describing parameters to the viewshed: * 'RADIUS' or 'RADIUS2': How far out from the viewpoint (in m) the viewshed operation is permitted to extend. Default: no limit. * 'HEIGHT': The height of the structure (in m). Default: 0.0 * 'WEIGHT': The numeric weight that this viewshed should be assigned when calculating visual quality. Default: 1.0 Returns: An unsorted list of the valid viewpoints and their metadata. The tuples themselves are in the order:: (viewpoint, radius, weight, height) Where * ``viewpoint``: a tuple of ``(projected x coord, projected y coord``) * ``radius``: the maximum radius of the viewshed * ``weight``: the weight of the viewshed (for calculating visual quality) * ``height``: The height of the structure at this point. """ dem_raster_info = pygeoprocessing.get_raster_info(dem_path) dem_nodata = dem_raster_info['nodata'][0] dem_gt = dem_raster_info['geotransform'] bbox_minx, bbox_miny, bbox_maxx, bbox_maxy = ( dem_raster_info['bounding_box']) # Use interleaved coordinates (xmin, ymin, xmax, ymax) spatial_index = rtree.index.Index(interleaved=True) structures_vector = gdal.OpenEx(structures_path, gdal.OF_VECTOR) for structures_layer_index in range(structures_vector.GetLayerCount()): structures_layer = structures_vector.GetLayer(structures_layer_index) layer_name = structures_layer.GetName() LOGGER.info('Layer %s has %s features', layer_name, structures_layer.GetFeatureCount()) radius_fieldname = None fieldnames = set( column.GetName() for column in structures_layer.schema) possible_radius_fieldnames = set( ['RADIUS', 'RADIUS2']).intersection(fieldnames) if possible_radius_fieldnames: radius_fieldname = possible_radius_fieldnames.pop() height_present = False height_fieldname = 'HEIGHT' if height_fieldname in fieldnames: height_present = True weight_present = False weight_fieldname = 'WEIGHT' if weight_fieldname in fieldnames: weight_present = True last_log_time = time.time() n_features_touched = -1 for point in structures_layer: n_features_touched += 1 if time.time() - last_log_time > 5.0: LOGGER.info( ("Checking structures in layer %s, approx. " "%.2f%%complete."), layer_name, 100.0 * (n_features_touched / structures_layer.GetFeatureCount())) last_log_time = time.time() # Coordinates in map units to pass to viewshed algorithm geometry = point.GetGeometryRef() viewpoint = (geometry.GetX(), geometry.GetY()) if (not bbox_minx <= viewpoint[0] <= bbox_maxx or not bbox_miny <= viewpoint[1] <= bbox_maxy): LOGGER.info( ('Feature %s in layer %s is outside of the DEM bounding ' 'box. Skipping.'), point.GetFID(), layer_name) continue max_radius = None if radius_fieldname: max_radius = math.fabs(point.GetField(radius_fieldname)) height = 0.0 if height_present: height = math.fabs(point.GetField(height_fieldname)) weight = 1.0 if weight_present: weight = float(point.GetField(weight_fieldname)) spatial_index.insert( point.GetFID(), (viewpoint[0], viewpoint[1], viewpoint[0], viewpoint[1]), {'max_radius': max_radius, 'weight': weight, 'height': height}) # Now check that the viewpoint isn't over nodata in the DEM. valid_structures = {} dem_origin_x = dem_gt[0] dem_origin_y = dem_gt[3] dem_pixelsize_x = dem_raster_info['pixel_size'][0] dem_pixelsize_y = dem_raster_info['pixel_size'][1] dem_raster = gdal.OpenEx(dem_path, gdal.OF_RASTER) dem_band = dem_raster.GetRasterBand(1) for block_data in pygeoprocessing.iterblocks((dem_path, 1), offset_only=True): # Using shapely.geometry.box here so that it'll handle the min/max for # us and all we need to define here are the correct coordinates. block_geom = shapely.geometry.box( dem_origin_x + dem_pixelsize_x * block_data['xoff'], dem_origin_y + dem_pixelsize_y * block_data['yoff'], dem_origin_x + dem_pixelsize_x * ( block_data['xoff'] + block_data['win_xsize']), dem_origin_y + dem_pixelsize_y * ( block_data['yoff'] + block_data['win_ysize'])) intersecting_points = list(spatial_index.intersection( block_geom.bounds, objects=True)) if len(intersecting_points) == 0: continue dem_block = dem_band.ReadAsArray(**block_data) for item in intersecting_points: viewpoint = (item.bounds[0], item.bounds[2]) metadata = item.object ix_viewpoint = int( (viewpoint[0] - dem_gt[0]) // dem_gt[1]) - block_data['xoff'] iy_viewpoint = int( (viewpoint[1] - dem_gt[3]) // dem_gt[5]) - block_data['yoff'] if utils.array_equals_nodata( numpy.array(dem_block[iy_viewpoint][ix_viewpoint]), dem_nodata).any(): LOGGER.info( 'Feature %s in layer %s is over nodata; skipping.', point.GetFID(), layer_name) continue if viewpoint in valid_structures: LOGGER.info( ('Feature %s in layer %s is a duplicate viewpoint. ' 'Skipping.'), point.GetFID(), layer_name) continue # if we've made it here, the viewpoint is valid. valid_structures[viewpoint] = metadata # Casting to a list so that taskgraph can pickle the result. return list( (point, meta['max_radius'], meta['weight'], meta['height']) for (point, meta) in valid_structures.items())