def from_geometry_to_record(self, geometry, record, default_srid=MODEL_SRID): if not geometry: return record # we can only deal with point. Getting the centroid should cover polygons point = geometry.centroid # convert the geometry in the record srid (if any) srid = self.cast_srid(record, default_srid=default_srid) datum, zone = (None, None) if srid: point.transform(srid) datum, zone = get_datum_and_zone(srid) # update record field record = record or {} if self.datum_field and datum: record[self.datum_field.name] = datum if self.zone_field and zone: record[self.zone_field.name] = zone if self.is_easting_northing and is_projected_srid(srid): if self.easting_field: record[self.easting_field.name] = point.x if self.northing_field: record[self.northing_field.name] = point.y elif self.is_lat_long and not is_projected_srid(srid): if self.longitude_field: record[self.longitude_field.name] = point.x if self.latitude_field: record[self.latitude_field.name] = point.y else: # what is going on here? # schema and datum/zone divergence? logger.warning( "Ambiguous schema and coordinate system. " "Cannot extract easting/northing from a spherical coordinate system " "or lat/long from a projected one. " "Schema: {}, srid: {}, record: {}".format( self.schema, srid, record)) return record
def test_geometry_to_data(self): project = self.project_1 client = self.custodian_1_client schema = self.schema_with_lat_long() dataset = self._create_dataset_with_schema(project, self.data_engineer_1_client, schema) self.assertIsNotNone(dataset.schema.latitude_field) self.assertIsNotNone(dataset.schema.longitude_field) self.assertIsNotNone(dataset.schema.datum_field) url = reverse('api:geometry-to-data', kwargs={'pk': dataset.pk}) # the geometry is required payload = {} resp = client.post(url, data=payload, format='json') self.assertEqual(resp.status_code, status.HTTP_400_BAD_REQUEST) # send a geometry and no data. new_geometry = {'type': 'Point', 'coordinates': [118, -34.0]} payload = {'geometry': new_geometry} resp = client.post(url, data=payload, format='json') self.assertEqual(resp.status_code, status.HTTP_200_OK) self.assertEqual(resp.get('content-type'), 'application/json') data = resp.json() self.assertTrue('geometry' in data) self.assertTrue('data' in data) # the returned datum should be the project expected_datum, expected_zone = constants.get_datum_and_zone( project.datum) expected_data = { 'Longitude': 118.0, 'Latitude': -34.0, 'Datum': expected_datum } self.assertEqual(data['data'], expected_data) expected_geometry = new_geometry self.assertEqual(data['geometry'], expected_geometry) # send geometry and data new_geometry = {'type': 'Point', 'coordinates': [118, -34.0]} new_data = { 'What': 'Updated What', 'When': '01/01/2001', 'Longitude': 0, 'Latitude': 0, 'Datum': 'WGS84' } payload = {'geometry': new_geometry, 'data': new_data} resp = client.post(url, data=payload, format='json') self.assertEqual(resp.status_code, status.HTTP_200_OK) self.assertEqual(resp.get('content-type'), 'application/json') data = resp.json() self.assertTrue('geometry' in data) self.assertTrue('data' in data) expected_data = { 'What': new_data['What'], 'When': new_data['When'], 'Longitude': 118.0, 'Latitude': -34.0, 'Datum': new_data['Datum'], } self.assertEqual(data['data'], expected_data) expected_geometry = new_geometry self.assertEqual(data['geometry'], expected_geometry)
def __init__(self, schema, project=None): if not isinstance(schema, GenericSchema): schema = GenericSchema(schema) self.schema = schema self.project = project self.errors = [] # Site Code self.site_code_field, errors = self._find_site_code_field() if errors: self.errors += errors # Datum self.datum_field, errors = find_unique_field( self.schema, BiosysSchema.DATUM_TYPE_NAME, ObservationSchema.DATUM_FIELD_NAME) if errors: self.errors += errors # Zone self.zone_field, errors = find_unique_field( self.schema, BiosysSchema.ZONE_TYPE_NAME, ObservationSchema.ZONE_FIELD_NAME) if errors: self.errors += errors # Latitude self.latitude_field, errors = find_unique_field( self.schema, BiosysSchema.LATITUDE_TYPE_NAME, ObservationSchema.LATITUDE_FIELD_NAME) if errors: self.errors += errors # Longitude self.longitude_field, errors = find_unique_field( self.schema, BiosysSchema.LONGITUDE_TYPE_NAME, ObservationSchema.LONGITUDE_FIELD_NAME) if errors: self.errors += errors # Easting self.easting_field, errors = find_unique_field( self.schema, BiosysSchema.EASTING_TYPE_NAME, ObservationSchema.EASTING_FIELD_NAME) if errors: self.errors += errors # Northing self.northing_field, errors = find_unique_field( self.schema, BiosysSchema.NORTHING_TYPE_NAME, ObservationSchema.NORTHING_FIELD_NAME) if errors: self.errors += errors # some post validations. # we need at least one method to get the geometry. if not any([ self.site_code_field, self.latitude_field, self.longitude_field, self.easting_field, self.northing_field ]): msg = "The schema must contain some geometry fields: latitude/longitude or easting/northing or " \ "alternatively a reference to the Site Code." self.errors.append(msg) # if we have a latitude we must have a longitude and vice-versa if not self.errors: if self.latitude_field and not self.longitude_field: self.errors.append("Missing Longitude field") if self.longitude_field and not self.latitude_field: self.errors.append("Missing Latitude field") # same for easting and northing if self.easting_field and not self.northing_field: self.errors.append("Missing Northing field") if self.northing_field and not self.easting_field: self.errors.append("Missing Easting field") # verify 'required' constraints: required constraints must be set if we are in 'single' mode. # e.g lat/long without site code or easting/northing if self.is_site_code_only and not self.site_code_field.required: self.errors.append(format_required_message(self.site_code_field)) if self.is_lat_long_only: if not self.latitude_field.required: self.errors.append(format_required_message( self.latitude_field)) if not self.longitude_field.required: self.errors.append( format_required_message(self.longitude_field)) if self.is_easting_northing_only: if not self.easting_field.required: self.errors.append(format_required_message(self.easting_field)) if not self.northing_field.required: self.errors.append(format_required_message( self.northing_field)) if not self.datum_field and (not self.zone_field or not self.zone_field.required): # Zone is mandatory if the project datum is not one with zone (projected). if self.project: datum, zone = get_datum_and_zone(project.datum) if zone is None: if not self.zone_field: msg = "Northing/easting coordinates require a zone," \ " but none has been supplied and the default datum for this project ({datum})" \ " does not include a zone. " \ "Either provide a zone in your data or " \ "change the default datum in the project to include a zone.".format(datum=datum) else: # zone field not set as required msg = "Northing/easting coordinates require a zone, " \ "but your zone field is not set as required and the default datum " \ "for this project ({datum}) does not include a zone. " \ "Either set the field as required or " \ "change the default datum in the project to include a zone.".format(datum=datum) self.errors.append(msg)