def isPpaWithinServiceArea(pal_records, ppa_zone_geometry): """Check if the ppa zone geometry with in service area then return True. Checks the ppa zone geometry's boundary and interior intersect only with the interior of the service area (not its boundary or exterior). Args: pal_records: A list of pal records to compute service area based on census_tracts. ppa_zone_geometry: A PPA polygon dictionary in GeoJSON format. Returns: A value is the boolean with the value as True if the ppa zone geometry's boundary and interior intersect with in the interior of the service area otherwise value as false. """ # Get the census tract for each pal record and convert it to Shapely # geometry. census_tracts_for_pal = [ utils.ToShapely( drive.census_tract_driver.GetCensusTract( pal['license']['licenseAreaIdentifier'])['features'][0] ['geometry']) for pal in pal_records ] pal_service_area = ops.cascaded_union(census_tracts_for_pal) # Convert GeoJSON dictionary to Shapely object. ppa_zone_shapely_geometry = utils.ToShapely(ppa_zone_geometry) return ppa_zone_shapely_geometry.buffer(-1e-6).within(pal_service_area)
def test_ClippedPpaByCensusWithSmallHoles(self): # Configuring for -96dBm circle above 40km wf_hybrid.CalcHybridPropagationLoss = testutils.FakePropagationPredictor( dist_type='REAL', factor=1.0, offset=(96+30-0.1) - 45.0) ppa_zone = ppa.PpaCreationModel(TestPpa.devices[1:], TestPpa.pal_records[1:]) ppa_zone = json.loads(ppa_zone) census_zone = utils.ToShapely( drive.census_tract_driver.GetCensusTract('06027000100') ['features'][0]['geometry']) self.assertTrue(utils.ToShapely(ppa_zone).buffer(-1e-6).within(census_zone))
def test_toshapely(self): poly = sgeo.Point(0, 0).buffer(1) poly_json = utils.ToGeoJson(poly) # Test for a generic geometry object. poly2 = utils.ToShapely(poly) self.assertEqual(poly2.difference(poly).area, 0) self.assertEqual(poly.difference(poly2).area, 0) # Test for a string geojson. poly2 = utils.ToShapely(poly_json) self.assertEqual(poly2.difference(poly).area, 0) self.assertEqual(poly.difference(poly2).area, 0)
def test_shrinks_polygon(self): with open(os.path.join(TEST_DIR, 'test_shrink.json'), 'r') as fd: ppa = json.load(fd) geometry = ppa['features'][0]['geometry'] self.assertTrue(geometry['type'], 'Polygon') spoly = utils.ToShapely(geometry) mpoly = sgeo.MultiPolygon( [sgeo.Point(0, 0).buffer(1), sgeo.Point(2, 0).buffer(0.1)]) poly1 = utils.ShrinkAndCleanPolygon(geometry, 1e-2) poly2 = utils.ShrinkAndCleanPolygon(spoly, 1e-2) self.assertTrue(poly2.area < spoly.area) with self.assertRaises(ValueError): poly = utils.ShrinkAndCleanPolygon(mpoly, 1e-2) self.assertEqual(poly1['type'], 'Polygon') self.assertTrue(isinstance(poly2, sgeo.Polygon)) spoly1 = utils.ToShapely(poly1) self.assertEqual(poly2.difference(spoly1).area, 0) self.assertEqual(spoly1.difference(poly2).area, 0)
def test_ClippedPpaByCensus(self): # Configuring for -96dBm circle above 40km wf_hybrid.CalcHybridPropagationLoss = testutils.FakePropagationPredictor( dist_type='REAL', factor=1.0, offset=(96+30-0.1) - 45.0) expected_ppa = sgeo.Polygon([(-80.3, 30.3), (-80.7, 30.3), (-80.7, 30.7), (-80.3, 30.7)]) ppa_zone = ppa.PpaCreationModel(TestPpa.devices, TestPpa.pal_records) ppa_zone = json.loads(ppa_zone) self.assertAlmostSamePolygon( utils.ToShapely(ppa_zone), expected_ppa, 0.001)
def test_SimplePpaCircle(self): # Configuring for -96dBm circle at 16km includes wf_hybrid.CalcHybridPropagationLoss = testutils.FakePropagationPredictor( dist_type='REAL', factor=1.0, offset=(96+30-0.1) - 16.0) expected_ppa = sgeo.Polygon( [vincenty.GeodesicPoint( TestPpa.devices[0]['installationParam']['latitude'], TestPpa.devices[0]['installationParam']['longitude'], dist_km=16.0, bearing=angle)[1::-1] # reverse to lng,lat for angle in xrange(360)]) ppa_zone = ppa.PpaCreationModel(TestPpa.devices, TestPpa.pal_records) ppa_zone = json.loads(ppa_zone) self.assertAlmostSamePolygon( utils.ToShapely(ppa_zone), expected_ppa, 0.001)
def test_grid_complex(self): with open(os.path.join(TEST_DIR, 'test_geocollection.json'), 'r') as fd: json_geo = json.load(fd) shape_geo = utils.ToShapely(json_geo) exp_pts = {(-95, 40), (-95.5, 40.5), (-95.5, 40), (-96, 40), (-96.5, 40.5), (-96.5, 40)} pts = utils.GridPolygon(json_geo, res_arcsec=1800) self.assertSetEqual(set(pts), exp_pts) pts = utils.GridPolygon(shape_geo, res_arcsec=1800) self.assertSetEqual(set(pts), exp_pts) pts = utils.GridPolygon(ops.unary_union(shape_geo), res_arcsec=1800) self.assertSetEqual(set(pts), exp_pts)
def getCbsdsWithinPolygon(cbsds, polygon): """Returns the list of all CBSDs within a polygon protection area. The returned CBSDs can lie either within the polygon or on its boundary. Args: cbsds: List of |CbsdData| dictionaries as defined in the SAS-SAS specification. polygon: A GeoJSON object containing polygon information. """ cbsds_within_polygon = [] polygon = utils.ToShapely(polygon['features'][0]['geometry']) for cbsd in cbsds: if not cbsd['grants']: continue cbsd_lat = cbsd['registration']['installationParam']['latitude'] cbsd_lon = cbsd['registration']['installationParam']['longitude'] point = sgeo.Point(cbsd_lon, cbsd_lat) # If the CBSD is within the polygon and has grants then add it to the list # TODO: check the need for touches() check if (polygon.contains(point) or polygon.touches(point)): cbsds_within_polygon.append(cbsd) return cbsds_within_polygon
def DpaProtectionPoints(dpa_name, dpa_geometry, protection_points_method=None): """Gets the protection points for a DPA. Args: dpa_name: The DPA name. dpa_geometry: The DPA geometry as a |shapely.Polygon/MultiPolygon or Point|. method: Three ways are supported for getting the protection points: + a path pointing to the file holding the protected points location defined as a geojson MultiPoint or Point geometry. The path can be either absolute or relative to the running script (normally the `harness/` directory). + 'default(<parameters>)': A simple default method. Parameters is a tuple defining the number of points to use for different part of the DPA: num_pts_front_border: Number of points in the front border num_pts_back_border: Number of points in the back border num_pts_front_zone: Number of points in the front zone num_pts_back_zone: Number of points in the back zone front_us_border_buffer_km: Buffering of US border for delimiting front/back. min_dist_front_border_pts_km: Minimum distance between front border points (km). min_dist_back_border_pts_km: Minimum distance between back border points (km). min_dist_front_zone_pts_km: Minimum distance between front zone points (km). min_dist_back_zone_pts_km: Minimum distance between back zone points (km). Example of encoding: 'default (200,50,20,5)' Note the default values are (25, 10, 10, 5, 40, 0.2, 1, 0.5, 3) Only the passed parameters will be redefined. The result are only approximate (actual distance and number of points may differ). + other 'my_method(p1, p2, ..pk)': The 'my_method` will be used, and passing to it the parameters p1, p2,... The method must have been registered as a builder method using `RegisterMethod()`. Returns: A list of protection points, each one being a (longitude, latitude) namedtuple. Raises: IOError: if the provided file cannot be found. ValueError: in case of other errors, such as invalid file or parameters. """ def _ExtractBuilder(method): idx = method.find('(') if idx == -1: return None name = method[:idx].strip().lower() params = method[idx:].strip() params = ast.literal_eval(params) return name, params if not protection_points_method: return _DefaultProtectionPoints(dpa_name, dpa_geometry) builder = _ExtractBuilder(protection_points_method) if builder: # Custom or default builder name, params = builder builder_fn = _builder_methods[name] protection_points = builder_fn(dpa_name, dpa_geometry, *params) else: # Using a geojson file protection_points_file = protection_points_method if not os.path.isfile(protection_points_file): raise IOError( 'Protected point definition file for DPA `%s` not found at location:' '%s' % (dpa_name, protection_points_file)) with open(protection_points_file, 'r') as fd: mpoints = utils.ToShapely(json.load(fd)) if isinstance(mpoints, sgeo.Point): mpoints = sgeo.MultiPoint([mpoints]) if not isinstance(mpoints, sgeo.MultiPoint): raise ValueError( 'Protected point definition file for DPA `%s` not a valid MultiPoint' ' geoJSON file.' % dpa_name) protection_points = [ ProtectionPoint(longitude=pt.x, latitude=pt.y) for pt in mpoints ] # Check validity of generated points if isinstance(dpa_geometry, sgeo.Point): if len(protection_points) != 1: raise ValueError( 'Multiple protection points for single point DPA `%s`.' % dpa_name) pt = sgeo.Point(protection_points[0].longitude, protection_points[0].latitude) if not pt.within(dpa_geometry.buffer(0.0005)): raise ValueError( 'Point for single point DPA `%s` is outside zone: %.5f %.5f' % (dpa_name, pt.x, pt.y)) else: mpoint = sgeo.MultiPoint([(pt.longitude, pt.latitude) for pt in protection_points]) mpoint_inside = dpa_geometry.buffer(0.0005).intersection(mpoint) if len(mpoint) != len(mpoint_inside): raise ValueError('Some points for DPA `%s` are outside zone.' % dpa_name) if not protection_points: raise ValueError('No valid points generated for DPA `%s`.' % dpa_name) return protection_points