class AdminBoundary(MPTTModel, models.Model): """ Represents a single administrative boundary (like a country, state or district) """ osm_id = models.CharField(max_length=15, unique=True, help_text="This is the OSM id for this administrative boundary") name = models.CharField(max_length=128, help_text="The name of our administrative boundary") level = models.IntegerField(help_text="The level of the boundary, 0 for country, 1 for state, 2 for district, 3 for ward") parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True, help_text="The parent to this political boundary if any") geometry = models.MultiPolygonField(null=True, help_text="The full geometry of this administrative boundary") simplified_geometry = models.MultiPolygonField(null=True, help_text="The simplified geometry of this administrative boundary") objects = models.GeoManager() @staticmethod def get_geojson_dump(features): # build a feature collection feature_collection = geojson.FeatureCollection(features) return geojson.dumps(feature_collection) def as_json(self): result = dict(osm_id=self.osm_id, name=self.name, level=self.level, aliases='') if self.parent: result['parent_osm_id'] = self.parent.osm_id aliases = '\n'.join([alias.name for alias in self.aliases.all()]) result['aliases'] = aliases return result def get_geojson_feature(self): return geojson.Feature(properties=dict(name=self.name, osm_id=self.osm_id, id=self.pk, level=self.level), zoomable=True if self.children.all() else False, geometry=None if not self.simplified_geometry else geojson.loads(self.simplified_geometry.geojson)) def get_geojson(self): return AdminBoundary.get_geojson_dump([self.get_geojson_feature()]) def get_children_geojson(self): children = [] for child in self.children.all(): children.append(child.get_geojson_feature()) return AdminBoundary.get_geojson_dump(children) def update(self, **kwargs): AdminBoundary.objects.filter(id=self.id).update(**kwargs) # if our name changed, update the category on any of our values name = kwargs.get('name', self.name) if name != self.name: from temba.values.models import Value Value.objects.filter(location_value=self).update(category=name) # update our object values so that self is up to date for key, value in kwargs.items(): setattr(self, key, value) def __str__(self): return "%s" % self.name
class Person(models.Model): gender = models.CharField(max_length=1, choices=GENDER_CHOICES) # Jards Macalé is an amazing brazilian musician! =] enjoy_jards_macale = models.BooleanField(default=True) like_metal_music = models.BooleanField(default=False) name = models.CharField(max_length=30) nickname = models.SlugField(max_length=36) age = models.IntegerField() bio = models.TextField() birthday = models.DateField() birth_time = models.TimeField() appointment = models.DateTimeField() blog = models.URLField() occupation = models.CharField(max_length=10, choices=OCCUPATION_CHOICES) uuid = models.UUIDField(primary_key=False) name_hash = models.BinaryField(max_length=16) days_since_last_login = models.BigIntegerField() duration_of_sleep = models.DurationField() email = models.EmailField() id_document = models.CharField(unique=True, max_length=10) try: from django.db.models import JSONField data = JSONField() except ImportError: # Skip JSONField-related fields pass try: from django.contrib.postgres.fields import ArrayField, HStoreField from django.contrib.postgres.fields import JSONField as PostgresJSONField from django.contrib.postgres.fields.citext import ( CICharField, CIEmailField, CITextField, ) from django.contrib.postgres.fields.ranges import ( BigIntegerRangeField, DateRangeField, DateTimeRangeField, IntegerRangeField, ) if settings.USING_POSTGRES: acquaintances = ArrayField(models.IntegerField()) postgres_data = PostgresJSONField() hstore_data = HStoreField() ci_char = CICharField(max_length=30) ci_email = CIEmailField() ci_text = CITextField() int_range = IntegerRangeField() bigint_range = BigIntegerRangeField() date_range = DateRangeField() datetime_range = DateTimeRangeField() except ImportError: # Skip PostgreSQL-related fields pass try: from django.contrib.postgres.fields.ranges import FloatRangeField if settings.USING_POSTGRES: float_range = FloatRangeField() except ImportError: # Django version greater or equal than 3.1 pass try: from django.contrib.postgres.fields.ranges import DecimalRangeField if settings.USING_POSTGRES: decimal_range = DecimalRangeField() except ImportError: # Django version lower than 2.2 pass if BAKER_GIS: geom = models.GeometryField() point = models.PointField() line_string = models.LineStringField() polygon = models.PolygonField() multi_point = models.MultiPointField() multi_line_string = models.MultiLineStringField() multi_polygon = models.MultiPolygonField() geom_collection = models.GeometryCollectionField()
class County(models.Model): name = models.CharField(max_length=25) state = models.ForeignKey(State) mpoly = models.MultiPolygonField(srid=4269) # Multipolygon in NAD83 objects = models.GeoManager()
class UserGeometryData(AbstractGeoManager): """holds user uploaded geometries""" gid = models.IntegerField(null=True) user_meta = models.ForeignKey(UserGeometryMetadata) desc = models.TextField(blank=True) geom = models.MultiPolygonField(srid=4326) @property def vertex_count(self): '''Return a count of the vertices for the geometry''' return self.geom.num_points def pathLocations(self, color='0x0000ff', weight=4, threshold=0.01): '''Returns a list of Google Maps Static API path Location strings for the geometry Note that only the first 2 dimensions of vertices are returned and they are ordered (lat,lon). Ref: http://code.google.com/apis/maps/documentation/staticmaps/#Paths ''' from contrib.glineenc.glineenc import encode_pairs geom = self.geom url = [ '&'.join( # join multiple polygons [ 'path=color:{color}|weight:{weight}|enc:{encoded_data}'. format(color=color, weight=weight, encoded_data=encode_pairs( points=tuple( [tuple(reversed(i)) for i in polygon]), threshold=threshold, )[0]) for polygon in multipolygon ]) for multipolygon in geom.coords ] return url def geom_gmap_static_url( self, color='0x0000ff', weight=4, width=512, height=256, ): '''Returns a Google Maps Static API URL representation of the geometry Refs: http://code.google.com/apis/maps/documentation/staticmaps/#Paths http://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm ''' MAX_PATH_LENGTH = 1930 # estimate the simplification threshold based on the number of vertices if self.vertex_count < 250: threshold = 0 # no simplification # elif self.vertex_count < 500: # threshold = 0.05 elif self.vertex_count < 2000: threshold = 0.05 else: threshold = 0.4 path_list = self.pathLocations(color=color, weight=weight, threshold=threshold) # sort so the geometries with the most vertices are first path_list.sort(key=len, reverse=True) pathLocations = '' for path in path_list: if len(pathLocations) < MAX_PATH_LENGTH: pathLocations += '&{path}'.format(path=path) url = ('http://maps.googleapis.com/maps/api/staticmap' '?size={width}x{height}' '&sensor=false' '{pathLocations}').format( color=color, weight=weight, width=width, height=height, pathLocations=pathLocations, ) return url
class CBSBuurt(models.Model): ogc_fid = models.AutoField(primary_key=True) bu_code = models.CharField(max_length=10, blank=True, null=True) bu_naam = models.CharField(max_length=60, blank=True, null=True) wk_code = models.CharField(max_length=8, blank=True, null=True) gm_code = models.CharField(max_length=6, blank=True, null=True) gm_naam = models.CharField(max_length=60, blank=True, null=True) ind_wbi = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) water = models.CharField(max_length=4, blank=True, null=True) postcode = models.CharField(max_length=10, blank=True, null=True) dek_perc = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) oad = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) sted = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) aant_inw = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) aant_man = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) aant_vrouw = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_00_14_jr = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_15_24_jr = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_25_44_jr = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_45_64_jr = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_65_eo_jr = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_ongehuwd = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_gehuwd = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_gescheid = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_verweduw = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) bev_dichth = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) aantal_hh = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_eenp_hh = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_hh_z_k = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_hh_m_k = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) gem_hh_gr = models.DecimalField(max_digits=11, decimal_places=1, blank=True, null=True) p_west_al = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_n_w_al = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_marokko = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_ant_aru = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_surinam = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_turkije = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) p_over_nw = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) opp_tot = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) opp_land = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) opp_water = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True) wkb_geometry = models.MultiPolygonField(blank=True, null=True) class Meta: managed = False db_table = 'gas_cbs_buurt_2017_raw' verbose_name = 'CBS buurt' verbose_name_plural = 'CBS buurten' def __str__(self): if self.bu_naam: return '{} - {}'.format(self.bu_naam, self.bu_code) else: return '{}'.format(self.bu_code)
class Job(TimeStampedModelMixin): """ Model for a Job. """ def __init__(self, *args, **kwargs): kwargs['the_geom'] = convert_polygon(kwargs.get('the_geom')) or '' kwargs['the_geom_webmercator'] = convert_polygon( kwargs.get('the_geom_webmercator')) or '' kwargs['the_geog'] = convert_polygon(kwargs.get('the_geog')) or '' super(Job, self).__init__(*args, **kwargs) id = models.AutoField(primary_key=True, editable=False) uid = models.UUIDField(unique=True, default=uuid.uuid4, editable=False, db_index=True) user = models.ForeignKey(User, related_name='owner') name = models.CharField(max_length=100, db_index=True) description = models.CharField(max_length=1000, db_index=True) event = models.CharField(max_length=100, db_index=True, default='', blank=True) region = models.ForeignKey(Region, null=True, on_delete=models.SET_NULL) provider_tasks = models.ManyToManyField(ProviderTask, related_name='provider_tasks') configs = models.ManyToManyField(ExportConfig, related_name='configs', blank=True) published = models.BooleanField(default=False, db_index=True) # publish export feature_save = models.BooleanField( default=False, db_index=True) # save feature selections feature_pub = models.BooleanField( default=False, db_index=True) # publish feature selections the_geom = models.MultiPolygonField(verbose_name='Extent for export', srid=4326, default='') the_geom_webmercator = models.MultiPolygonField( verbose_name='Mercator extent for export', srid=3857, default='') the_geog = models.MultiPolygonField( verbose_name='Geographic extent for export', geography=True, default='') objects = models.GeoManager() include_zipfile = models.BooleanField(default=False) json_tags = JSONField(default=dict) class Meta: # pragma: no cover managed = True db_table = 'jobs' def save(self, *args, **kwargs): self.the_geom = convert_polygon(self.the_geom) self.the_geog = GEOSGeometry(self.the_geom) self.the_geom_webmercator = self.the_geom.transform(ct=3857, clone=True) super(Job, self).save(*args, **kwargs) def __str__(self): return '{0}'.format(self.name) @property def overpass_extents(self, ): """ Return the export extents in order required by Overpass API. """ extents = GEOSGeometry(self.the_geom).extent # (w,s,e,n) # overpass needs extents in order (s,w,n,e) overpass_extents = '{0},{1},{2},{3}'.format(str(extents[1]), str(extents[0]), str(extents[3]), str(extents[2])) return overpass_extents @property def extents(self, ): return GEOSGeometry(self.the_geom).extent # (w,s,e,n) @property def filters(self, ): """ Return key=value pairs for each tag in this export. Used in utils.overpass.filter to filter the export. """ # Command-line key=value filters for osmfilter filters = [] for tag in self.json_tags: kv = '{0}={1}'.format(tag['key'], tag['value']) filters.append(kv) return filters @property def categorised_tags(self, ): """ Return tags mapped according to their geometry types. """ points = set() lines = set() polygons = set() for tag in self.json_tags: if 'point' in tag['geom']: points.add(tag['key']) if 'line' in tag['geom']: lines.add(tag['key']) if 'polygon' in tag['geom']: polygons.add(tag['key']) return { 'points': sorted(list(points)), 'lines': sorted(list(lines)), 'polygons': sorted(list(polygons)) } @property def bounds_geojson(self, ): return serialize('geojson', [self], geometry_field='the_geom', fields=('name', 'the_geom'))
class DataProvider(UIDMixin, TimeStampedModelMixin, CachedModelMixin): """ Model for a DataProvider. """ name = models.CharField(verbose_name="Service Name", unique=True, max_length=100) slug = LowerCaseCharField(max_length=40, unique=True, default="") label = models.CharField(verbose_name="Label", max_length=100, null=True, blank=True) url = models.CharField( verbose_name="Service URL", max_length=1000, null=True, default="", blank=True, help_text= "The SERVICE_URL is used as the endpoint for WFS, OSM, and WCS services. It is " "also used to check availability for all OGC services. If you are adding a TMS " "service, please provide a link to a single tile, but with the coordinate numbers " "replaced by {z}, {y}, and {x}. Example: https://tiles.your-geospatial-site.com/" "tiles/default/{z}/{y}/{x}.png", ) preview_url = models.CharField( verbose_name="Preview URL", max_length=1000, null=True, default="", blank=True, help_text= "This url will be served to the front end for displaying in the map.", ) service_copyright = models.CharField( verbose_name="Copyright", max_length=2000, null=True, default="", blank=True, help_text= "This information is used to display relevant copyright information.", ) service_description = models.TextField( verbose_name="Description", null=True, default="", blank=True, help_text= "This information is used to provide information about the service.", ) layer = models.CharField(verbose_name="Service Layer", max_length=100, null=True, blank=True) export_provider_type = models.ForeignKey(DataProviderType, verbose_name="Service Type", null=True, on_delete=models.CASCADE) max_selection = models.DecimalField( verbose_name="Max selection area", default=250, max_digits=12, decimal_places=3, help_text= "This is the maximum area in square kilometers that can be exported " "from this provider in a single DataPack.", ) level_from = models.IntegerField( verbose_name="Seed from level", default=0, null=True, blank=True, help_text= "This determines the starting zoom level the tile export will seed from.", ) level_to = models.IntegerField( verbose_name="Seed to level", default=10, null=True, blank=True, help_text= "This determines the highest zoom level the tile export will seed to.", ) config = models.TextField( default="", null=True, blank=True, verbose_name="Configuration", help_text= """WMS, TMS, WMTS, and ArcGIS-Raster require a MapProxy YAML configuration with a Sources key of imagery and a Service Layer name of imagery; the validator also requires a layers section, but this isn't used. OSM Services also require a YAML configuration.""", ) DATA_TYPES = [ (GeospatialDataType.VECTOR.value, ("Vector")), (GeospatialDataType.RASTER.value, ("Raster")), (GeospatialDataType.ELEVATION.value, ("Elevation")), (GeospatialDataType.MESH.value, ("Mesh")), (GeospatialDataType.POINT_CLOUD.value, ("Point Cloud")), ] data_type = models.CharField( choices=DATA_TYPES, max_length=20, verbose_name="Data Type", null=True, default="", blank=True, help_text="The type of data provided (e.g. elevation, raster, vector)", ) user = models.ForeignKey(User, related_name="+", null=True, default=None, blank=True, on_delete=models.CASCADE) license = models.ForeignKey(License, related_name="+", null=True, blank=True, default=None, on_delete=models.CASCADE) zip = models.BooleanField(default=False) display = models.BooleanField(default=False) thumbnail = models.ForeignKey( MapImageSnapshot, blank=True, null=True, on_delete=models.SET_NULL, help_text="A thumbnail image generated to give a high level" " preview of what a provider's data looks like.", ) attribute_class = models.ForeignKey( AttributeClass, blank=True, null=True, on_delete=models.SET_NULL, related_name="data_providers", help_text= "The attribute class is used to limit users access to resources using this data provider.", ) the_geom = models.MultiPolygonField( verbose_name="Covered Area", srid=4326, default= "SRID=4326;MultiPolygon (((-180 -90,180 -90,180 90,-180 90,-180 -90)))", ) # Used to store user list of user caches so that they can be invalidated. provider_caches_key = "data_provider_caches" class Meta: # pragma: no cover managed = True db_table = "export_provider" # Check if config changed to updated geometry __config = None __url = None __layer = None def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.__config = self.config self.__url = self.url self.__layer = self.layer def update_geom(self): from eventkit_cloud.tasks.helpers import download_data from eventkit_cloud.ui.helpers import file_to_geojson geometry = None if self.config != self.__config: orig_extent_url = load_provider_config( self.__config).get("extent_url") config = load_provider_config(self.config) extent_url = config.get("extent_url") if extent_url and extent_url != orig_extent_url: random_uuid = uuid.uuid4() session = get_or_update_session(**config) if not extent_url: return output_file = download_data(task_uid=str(random_uuid), input_url=extent_url, session=session) geojson = file_to_geojson(output_file) geojson_geometry = geojson.get("geometry") or geojson.get( "features", [{}])[0].get("geometry") geometry = GEOSGeometry(json.dumps(geojson_geometry), srid=4326) elif (self.url != self.__url) or (self.layer != self.__layer): try: client = self.get_service_client() geometry = client.download_geometry() except AttributeError as e: # TODO: This fails in tests. How to handle failure to update geometry? logger.info(e, exc_info=True) if geometry: self.the_geom = convert_polygon(geometry) def get_service_client(self) -> GisClient: url = self.url if not self.url and "osm" in self.export_provider_type.type_name: logger.error( "Use of settings.OVERPASS_API_URL is deprecated and will be removed in 1.13" ) url = settings.OVERPASS_API_URL Client = get_client(self.export_provider_type.type_name) return Client(url, self.layer, aoi_geojson=None, slug=self.slug, config=load_provider_config(self.config)) def check_status(self, aoi_geojson: dict = None): try: client = self.get_service_client() response = client.check(aoi_geojson=aoi_geojson) except Exception as e: logger.error(e, exc_info=True) response = get_status_result(CheckResult.UNKNOWN_ERROR) logger.error( f"An exception occurred while checking the {self.name} provider.", exc_info=True) logger.info(f"Status of provider '{self.name}': {response}") return response def save(self, force_insert=False, force_update=False, *args, **kwargs): # Something is closing the database connection which is raising an error. # Using a separate process allows the connection to be closed in separate process while leaving it open. proc = multiprocessing.dummy.Process(target=self.update_geom) proc.start() proc.join() if not self.slug: self.slug = self.name.replace(" ", "_").lower() if len(self.slug) > 40: self.slug = self.slug[0:39] cache.delete(f"base-config-{self.slug}") super(DataProvider, self).save(force_insert, force_update, *args, **kwargs) def __str__(self): return "{0}".format(self.name) @property def metadata(self): from eventkit_cloud.utils.mapproxy import get_mapproxy_metadata_url if not self.config: return None config = yaml.load(self.config) url = config.get("sources", {}).get("info", {}).get("req", {}).get("url") type = config.get("sources", {}).get("info", {}).get("type") if url: return {"url": get_mapproxy_metadata_url(self.slug), "type": type} @property def footprint_url(self): from eventkit_cloud.utils.mapproxy import get_mapproxy_footprint_url if not self.config: return None config = yaml.load(self.config) url = config.get("sources", {}).get("footprint", {}).get("req", {}).get("url") if url: return get_mapproxy_footprint_url(self.slug) @property def layers(self) -> List[str]: """ Used to populate the list of vector layers, typically for contextual or styling information. :return: A list of layer names. """ if self.data_type != GeospatialDataType.VECTOR.value: return [] if not self.config: # Often layer names are configured using an index number but this number is not very # useful when using the data so fall back to the slug which should be more meaningful. if not self.layer: # check for NoneType or empty string return [self.slug] try: int(self.layer) return [ self.slug ] # self.layer is an integer, so use the slug for better context. except ValueError: return [ self.layer ] # If we got here, layer is not None or an integer so use that. config = clean_config(self.config, return_dict=True) # As of EK 1.9.0 only vectors support multiple layers in a single provider if self.export_provider_type.type_name in ["osm", "osm-generic"]: return list(config.keys()) else: return [ layer.get("name") for layer in config.get("vector_layers", []) ] def get_use_bbox(self): if self.export_provider_type is not None: return self.export_provider_type.use_bbox else: return False """ Max datasize is the size in megabytes. """ @property def max_data_size(self): config = yaml.load(self.config) return None if config is None else config.get("max_data_size", None) def get_max_data_size(self, user=None): if not user: return self.max_data_size # the usersizerule set is looped instead of using a queryset filter so that it can be prefetched. if user: user_size_rule = list( filter(lambda user_size_rule: user_size_rule.user == user, self.usersizerule_set.all())) if user_size_rule: return user_size_rule[0].max_data_size return self.max_data_size def get_max_selection_size(self, user=None): if not user: return self.max_selection # the usersizerule set is looped instead of using a queryset filter so that it can be prefetched. if user: user_size_rule = list( filter(lambda user_size_rule: user_size_rule.user == user, self.usersizerule_set.all())) if user_size_rule: return user_size_rule[0].max_selection_size return self.max_selection def get_data_type(self) -> str: """ This is used to populate the run metadata with special types for OSM and NOME. This is used for custom cartography, and should be removed if custom cartography is made configurable. :param data_provider: :return: """ if self.slug.lower() in ["nome", "osm"]: return self.slug.lower() else: return str(self.data_type)
class WalkInAccess(models.Model): unit_number = models.IntegerField() land_type = models.IntegerField() polygon = models.MultiPolygonField() center_point = models.PolygonField() square_feet = models.FloatField(default=0.0)
class UserData(gismodels.Model): user_shape = models.ForeignKey("geodata.UserProjectShape", on_delete=models.CASCADE) geometry = gismodels.MultiPolygonField(srid=4326)
class TimeZone(models.Model): objects = models.Manager() name = models.CharField(max_length=256) bounds = models.MultiPolygonField(blank=True, null=True)
class PhoneAreaCode(models.Model): objects = models.Manager() name = models.CharField(max_length=256) bounds = models.MultiPolygonField(blank=True, null=True) center = models.PointField(blank=True, null=True)
class Region(MPTTModel, gismodels.Model): name = models.CharField( max_length=50, unique=True, verbose_name='Название', ) short_name = models.CharField(max_length=150, unique=True, verbose_name='Сокращенное название') slug_name = models.SlugField( max_length=100, unique=True, verbose_name='Идентификатор', ) parent = TreeForeignKey( 'self', on_delete=models.CASCADE, null=True, blank=True, related_name='children', verbose_name='Входит в', ) borders = gismodels.MultiPolygonField( srid=4326, null=True, blank=True, verbose_name='границы региона', ) map_center = gismodels.PointField( srid=4326, null=True, blank=True, verbose_name='центр карты', ) geojson_file = models.FileField( upload_to=region_directory_path, blank=True, null=True, verbose_name='geojson файл границ региона', ) def save(self, *args, **kwargs): if file_exists(self.geojson_file): with self.geojson_file.open(mode='r') as file_handler: borders = file_handler.read() self.borders = borders # if self.geojson_file and file_exists(self.geojson_file): # self.geojson_file.open(mode='rb') if self.borders: regions = Region.objects.annotate(center=Centroid('borders')) self.map_center = regions.get(pk=self.pk).center super().save(*args, **kwargs) def __str__(self): return self.name class MPTTMeta: order_insertion_by = ['name'] class Meta: verbose_name = 'Регион' verbose_name_plural = 'Регион'
class County(NamedModel): state = models.ForeignKey(State, models.CASCADE) mpoly = models.MultiPolygonField(srid=4269) # Multipolygon in NAD83
class Territorio(models.Model): TERRITORIO = Choices( ('C', 'Comune'), ('P', 'Provincia'), ('R', 'Regione'), ('N', 'Nazionale'), ('E', 'Estero'), ) cod_reg = models.IntegerField(default=0, blank=True, null=True, db_index=True) cod_prov = models.IntegerField(default=0, blank=True, null=True, db_index=True) cod_com = models.IntegerField(default=0, blank=True, null=True, db_index=True) denominazione = models.CharField(max_length=128, db_index=True) denominazione_ted = models.CharField(max_length=128, blank=True, null=True, db_index=True) slug = AutoSlugField(populate_from='nome_per_slug', max_length=256, unique=True, db_index=True) territorio = models.CharField(max_length=1, choices=TERRITORIO, db_index=True) geom = models.MultiPolygonField(srid=4326, null=True, blank=True) popolazione_totale = models.IntegerField(null=True, blank=True) popolazione_maschile = models.IntegerField(null=True, blank=True) popolazione_femminile = models.IntegerField(null=True, blank=True) objects = TerritorioManager() @property def nome(self): if self.denominazione_ted: return u'{} - {}'.format(self.denominazione, self.denominazione_ted) else: return u'{}'.format(self.denominazione) @property def nome_completo(self): if self.is_comune or self.is_provincia: return u'{} di {}'.format(self.get_territorio_display(), self.nome) elif self.is_regione: return u'{} {}'.format(self.get_territorio_display(), self.nome) else: return u'{}'.format(self.nome) @property def nome_con_provincia(self): if self.is_provincia: return u'{} (Provincia)'.format(self.nome) else: return u'{}'.format(self.nome) @property def nome_per_slug(self): return u'{} {}'.format(self.denominazione, self.get_territorio_display()) @property def ambito_territoriale(self): """ Returns: a Region (for C,P or R), Nazionale, or Estero """ if self.is_regione: return self.nome elif self.regione: return self.regione.nome else: return self.get_territorio_display() @cached_property def regione(self): if self.is_comune or self.is_provincia: return self.__class__.objects.regioni_by_cod[self.cod_reg] else: return None @cached_property def provincia(self): if self.is_comune: return self.__class__.objects.provincie_by_cod[self.cod_prov] else: return None @property def codice(self): if self.is_comune: return self.cod_com elif self.is_provincia: return self.cod_prov else: return self.cod_reg def progetti(self): return self.progetto_set.all() @property def n_progetti(self): return self.progetto_set.count() @property def code(self): return self.get_cod_dict().values()[0] def get_cod_dict(self, prefix=''): """ Return a dict with {prefix}cod_{type} key initialized with correct value """ if self.is_comune: return {'{}cod_com'.format(prefix): self.cod_com} elif self.is_provincia: return {'{}cod_prov'.format(prefix): self.cod_prov} elif self.is_regione: return {'{}cod_reg'.format(prefix): self.cod_reg} elif self.is_nazionale: return {'{}cod_reg'.format(prefix): 0} elif self.is_estero: return {'{}pk'.format(prefix): self.pk} raise Exception('Territorio non interrogabile {}'.format(self)) def get_progetti_search_url(self, **kwargs): """ Returns the correct search url in progetti faceted navigation. Can be used with optional filters: tema=TemaInstance natura=ClassificazioneAzioneInstance """ search_url = reverse('progetti_search') + '?q=' if 'tema' in kwargs: tema = kwargs['tema'] search_url += '&selected_facets=tema:{}'.format(tema.codice) if 'natura' in kwargs: natura = kwargs['natura'] search_url += '&selected_facets=natura:{}'.format(natura.codice) if 'programma' in kwargs: programma = kwargs['programma'] search_url += '&fonte_fin={}'.format(programma.codice) if 'gruppo_programmi' in kwargs: gruppo_programmi = kwargs['gruppo_programmi'] search_url += '&gruppo_programmi={}'.format( gruppo_programmi.codice) d = self.get_cod_dict() key = d.keys()[0] search_url += '&territorio{}={}'.format(key[3:], d[key]) return search_url def get_absolute_url(self): url_name = 'territori_{}'.format(self.get_territorio_display().lower()) if self.is_nazionale or self.is_estero: return reverse(url_name) else: return reverse(url_name, kwargs={'slug': self.slug}) def __getattr__(self, item): match = re.search( '^is_({})$'.format('|'.join( dict(self.__class__.TERRITORIO).values()).lower()), item) if match: return self.get_territorio_display().lower() == match.group(1) else: raise AttributeError('{0!r} object has no attribute {1!r}'.format( self.__class__.__name__, item)) def __unicode__(self): return u'{}'.format(self.nome) class Meta: verbose_name = u'Località' verbose_name_plural = u'Località' ordering = ['denominazione']
from django.contrib.gis.db import models from django.db import connection, migrations ops = [ migrations.CreateModel( name='Neighborhood', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=100, unique=True)), ('geom', models.MultiPolygonField(srid=4326)), ], options={}, bases=(models.Model, ), ), migrations.CreateModel( name='Household', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('neighborhood', models.ForeignKey( 'gis_migrations.Neighborhood', models.SET_NULL, to_field='id',
class AdminBoundary(MPTTModel, models.Model): """ Represents a single administrative boundary (like a country, state or district) """ LEVEL_COUNTRY = 0 LEVEL_STATE = 1 LEVEL_DISTRICT = 2 LEVEL_WARD = 3 # used to separate segments in a hierarchy of boundaries. Has the advantage of being a character in GSM7 and # being very unlikely to show up in an admin boundary name. PATH_SEPARATOR = ">" PADDED_PATH_SEPARATOR = " > " osm_id = models.CharField( max_length=15, unique=True, help_text="This is the OSM id for this administrative boundary" ) name = models.CharField(max_length=128, help_text="The name of our administrative boundary") level = models.IntegerField( help_text="The level of the boundary, 0 for country, 1 for state, 2 for district, 3 for ward" ) parent = TreeForeignKey( "self", null=True, on_delete=models.PROTECT, blank=True, related_name="children", db_index=True, help_text="The parent to this political boundary if any", ) path = models.CharField(max_length=768, help_text="The full path name for this location") geometry = models.MultiPolygonField(null=True, help_text="The full geometry of this administrative boundary") simplified_geometry = models.MultiPolygonField( null=True, help_text="The simplified geometry of this administrative boundary" ) objects = NoGeometryManager() geometries = GeometryManager() @staticmethod def get_geojson_dump(features): # build a feature collection feature_collection = geojson.FeatureCollection(features) return geojson.dumps(feature_collection) def as_json(self): result = dict(osm_id=self.osm_id, name=self.name, level=self.level, aliases="") if self.parent: result["parent_osm_id"] = self.parent.osm_id aliases = "\n".join([alias.name for alias in self.aliases.all()]) result["aliases"] = aliases return result def get_geojson_feature(self): return geojson.Feature( properties=dict(name=self.name, osm_id=self.osm_id, id=self.pk, level=self.level), zoomable=True if self.children.all() else False, geometry=None if not self.simplified_geometry else geojson.loads(self.simplified_geometry.geojson), ) def get_geojson(self): return AdminBoundary.get_geojson_dump([self.get_geojson_feature()]) def get_children_geojson(self): children = [] for child in self.children.all(): children.append(child.get_geojson_feature()) return AdminBoundary.get_geojson_dump(children) def update(self, **kwargs): AdminBoundary.objects.filter(id=self.id).update(**kwargs) # update our object values so that self is up to date for key, value in kwargs.items(): setattr(self, key, value) def update_path(self): if self.level == 0: self.path = self.name self.save(update_fields=("path",)) def _update_child_paths(boundary): boundaries = AdminBoundary.objects.filter(parent=boundary).only("name", "parent__path") boundaries.update( path=Concat(Value(boundary.path), Value(" %s " % AdminBoundary.PATH_SEPARATOR), F("name")) ) for boundary in boundaries: _update_child_paths(boundary) _update_child_paths(self) def release(self): AdminBoundary.objects.filter(parent=self).update(parent=None) self.delete() @classmethod def create(cls, osm_id, name, level, parent=None, **kwargs): """ Create method that takes care of creating path based on name and parent """ path = name if parent is not None: path = parent.path + AdminBoundary.PADDED_PATH_SEPARATOR + name return AdminBoundary.objects.create(osm_id=osm_id, name=name, level=level, parent=parent, path=path, **kwargs) @classmethod def strip_last_path(cls, path): """ Strips the last part of the passed in path. Throws if there is no separator """ parts = path.split(AdminBoundary.PADDED_PATH_SEPARATOR) if len(parts) <= 1: # pragma: no cover raise Exception("strip_last_path called without a path to strip") return AdminBoundary.PADDED_PATH_SEPARATOR.join(parts[:-1]) @classmethod def get_by_path(cls, org, path): cache = getattr(org, "_abs", {}) if not cache: setattr(org, "_abs", cache) boundary = cache.get(path) if not boundary: boundary = AdminBoundary.objects.filter(path=path).first() cache[path] = boundary return boundary def __str__(self): return "%s" % self.name
class ITreeRegion(ITreeRegionAbstract, models.Model): code = models.CharField(max_length=40, unique=True) geometry = models.MultiPolygonField(srid=3857) objects = models.GeoManager()
class Location(MPTTModel): """ Represents Location, either a point or geospatial object, pcode should be unique Relates to :model:`locations.GatewayType` """ name = models.CharField(verbose_name=_("Name"), max_length=254) gateway = models.ForeignKey(GatewayType, verbose_name=_('Location Type')) latitude = models.FloatField( verbose_name=_("Latitude"), null=True, blank=True, ) longitude = models.FloatField( verbose_name=_("Longitude"), null=True, blank=True, ) p_code = models.CharField( verbose_name=_("P Code"), max_length=32, blank=True, null=True, ) parent = TreeForeignKey( 'self', verbose_name=_("Parent"), null=True, blank=True, related_name='children', db_index=True, ) geom = models.MultiPolygonField( verbose_name=_("Geo Point"), null=True, blank=True, ) point = models.PointField(verbose_name=_("Point"), null=True, blank=True) created = AutoCreatedField(_('created')) modified = AutoLastModifiedField(_('modified')) objects = LocationManager() def __str__(self): # TODO: Make generic return u'{} ({} {}: {})'.format( self.name, self.gateway.name, 'CERD' if self.gateway.name == 'School' else 'PCode', self.p_code if self.p_code else '') @property def geo_point(self): return self.point if self.point else self.geom.point_on_surface if self.geom else "" @property def point_lat_long(self): return "Lat: {}, Long: {}".format(self.point.y, self.point.x) class Meta: unique_together = ('name', 'gateway', 'p_code') ordering = ['name']
class DivisionGeography(models.Model): division = models.OneToOneField(OrganisationDivision, related_name="geography", on_delete=models.CASCADE) geography = models.MultiPolygonField() source = models.CharField(blank=True, max_length=255)
class WeatherWarnings(models.Model): """ Weather Warnings from NOAA """ service_id_warnings = 0 service_id_watches = 1 prod_type = models.CharField(max_length=200, null=True, blank=True) oid = models.CharField(max_length=38, null=True, blank=True) idp_source = models.CharField(max_length=200, null=True, blank=True) idp_subset = models.CharField(max_length=200, null=True, blank=True) url = models.CharField(max_length=500, null=True, blank=True) event = models.CharField(max_length=200, null=True, blank=True) wfo = models.CharField(max_length=200, null=True, blank=True) warnid = models.CharField(max_length=200, null=False, blank=True) phenom = models.CharField(max_length=200, null=True, blank=True) sig = models.CharField(max_length=200, null=True, blank=True) expiration = models.DateTimeField(null=True, blank=True) idp_ingestdate = models.DateTimeField(null=True, blank=True) issuance = models.DateTimeField(null=True, blank=True) warngeom = models.MultiPolygonField(null=True) @classmethod def create_warning(cls, warning): """ create a weather warning """ warn = WeatherWarnings(prod_type=warning['prod_type'], url=warning['url'], warngeom=warning['warngeom'], warnid=warning['warnid']) warn.save() return warn @property def origin_uri(self): """ This object's URI (from the NOAA map service ) 2017-09-18T01:00:00+00:00 time format """ return 'https://idpgis.ncep.noaa.gov/arcgis/rest/services/NWS_Forecasts_Guidance_Warnings/watch_warn_adv/MapServer/1/{0}?f=json' \ .format(self.objectid) @classmethod def load_warning_data(cls): # sometimes error/empty objects = WeatherWarnings.requests_retry_session().get('https://idpgis.ncep.noaa.gov/arcgis/rest/services/NWS_Forecasts_Guidance_Warnings/watch_warn_adv/MapServer/1/query?' 'where=1=1&geometry=&geometryType=esriGeometryEnvelope&inSR=&spatialRel=esriSpatialRelIntersects&outFields=*&returnGeometry=false' '&returnTrueCurves=false&outSR=&returnIdsOnly=true&returnCountOnly=false&returnZ=false&returnM=false&returnDistinctValues=false&f=json', timeout=25) print objects.content object_ids = set(json.loads(objects.content)['objectIds']) url = 'https://idpgis.ncep.noaa.gov/arcgis/rest/services/NWS_Forecasts_Guidance_Warnings/watch_warn_adv/MapServer/1/{0}?f=json' for object in object_ids: try: obj = WeatherWarnings.requests_retry_session().get(url.format(object), timeout=15) obj = json.loads(obj.content) data = dict((k.lower(), v) for k, v in obj['feature']['attributes'].iteritems()) warninggeom = '' datapost = {} # Check if warning is already loaded and update as needed if WeatherWarnings.objects.filter(warnid=data['warnid']): datapost = WeatherWarnings.objects.filter(warnid=data['warnid']) warningfeature = datapost[0] if data['expiration'] != " ": warningfeature.expiration = date_parse(data['expiration']) if obj['feature'].get('geometry'): poly = map(LinearRing, obj['feature']['geometry']['rings']) warningfeature.warngeom = MultiPolygon(fromstr(str(Polygon(*poly).simplify(0.0001))),) warninggeom = MultiPolygon(fromstr(str(Polygon(*poly))),) warningfeature.issuance = date_parse(data['issuance']) warningfeature.idp_subset = data['idp_subset'] print data['warnid'] + " Updated" else: datapost['prod_type'] = data['prod_type'] datapost['idp_source'] = data['idp_source'] datapost['sig'] = data['sig'] datapost['wfo'] = data['wfo'] datapost['url'] = data['url'] datapost['phenom'] = data['phenom'] if data['expiration'] != " ": datapost['expiration'] = date_parse(data['issuance']) if obj['feature'].get('geometry'): poly = map(LinearRing, obj['feature']['geometry']['rings']) datapost['warngeom'] = MultiPolygon(fromstr(str(Polygon(*poly).simplify(0.0003))),) warninggeom = MultiPolygon(fromstr(str(Polygon(*poly))),) datapost['issuance'] = date_parse(data['issuance']) datapost['idp_subset'] = data['idp_subset'] datapost['warnid'] = data['warnid'] warningfeature = cls.objects.create(**datapost) print 'Created Warning: {0}'.format(data.get('warnid')) warningfeature.save() # Intersect with Departments and update table if overlap if(warninggeom != ''): intersectDepartmentList = FireDepartment.objects.filter(geom__intersects=warninggeom) if(intersectDepartmentList.count() > 0): WeatherWarnings.add_warnings_to_departments(intersectDepartmentList, warningfeature) print "Total intersecting Departments " + str(intersectDepartmentList.count()) except KeyError: print '{0} failed.'.format(object) print url.format(object) except IntegrityError: print '{0} failed.'.format(object) print url.format(object) print sys.exc_info() try: rollback() except: pass except: print '{0} failed.'.format(object) print url.format(object) print sys.exc_info() print '{0} Total Weather Warnings.'.format(WeatherWarnings.objects.all().count()) @classmethod def add_warnings_to_departments(self, departmentQuerySet, weatherWarnings): """ adds and updates departement weather warnings """ for fireDept in departmentQuerySet: try: print(weatherWarnings.prod_type + " for " + fireDept.name) print("Warning Expires " + str(weatherWarnings.expiration.strftime('%c'))) # Check if warning is already loaded and update as needed if DepartmentWarnings.objects.filter(warningname=weatherWarnings.warnid, departmentfdid=fireDept.id): dataduplicate = DepartmentWarnings.objects.filter(warningname=weatherWarnings.warnid, departmentfdid=fireDept.id) deptupdate = dataduplicate[0] deptupdate.warningname = weatherWarnings.warnid deptupdate.prod_type = weatherWarnings.prod_type deptupdate.expiredate = weatherWarnings.expiration deptupdate.issuedate = weatherWarnings.issuance deptupdate.url = weatherWarnings.url deptupdate.warngeom = weatherWarnings.warngeom deptupdate.save() print weatherWarnings.warnid + ' Department Warning Updated for ' + fireDept.name else: dataadd = {} dataadd['department'] = fireDept dataadd['departmentfdid'] = fireDept.id dataadd['departmentname'] = fireDept.name dataadd['warningfdid'] = weatherWarnings dataadd['warningname'] = weatherWarnings.warnid dataadd['prod_type'] = weatherWarnings.prod_type dataadd['expiredate'] = weatherWarnings.expiration dataadd['issuedate'] = weatherWarnings.issuance dataadd['url'] = weatherWarnings.url dataadd['warngeom'] = weatherWarnings.warngeom deptupdate = DepartmentWarnings.objects.create(**dataadd) deptupdate.save() print 'Department Warning Created' except: print 'Error running Department Query' return @property def warning_area(self): """ Project data as needed """ if self.district: try: return (self.warngeom.transform(102009, clone=True).area / 1000000) * 0.38610 except: return @classmethod def requests_retry_session(self, retries=2, backoff_factor=0.3, status_forcelist=(500, 502, 504), session=None): session = session or requests.Session() retry = Retry( total=retries, read=retries, connect=retries, backoff_factor=backoff_factor, status_forcelist=status_forcelist, ) adapter = HTTPAdapter(max_retries=retry) session.mount('http://', adapter) session.mount('https://', adapter) return session class Meta: verbose_name = 'Weather Warnings'
class Job(UIDMixin, TimeStampedModelMixin): """ Model for a Job. """ # the "choices" setting for the django admin page drop down list requires a type that can be indexed visibility_choices = [] for value in VisibilityState: visibility_choices.append((value.value, value.value)) def __init__(self, *args, **kwargs): kwargs["the_geom"] = convert_polygon(kwargs.get("the_geom")) or "" kwargs["the_geom_webmercator"] = convert_polygon( kwargs.get("the_geom_webmercator")) or "" kwargs["the_geog"] = convert_polygon(kwargs.get("the_geog")) or "" super(Job, self).__init__(*args, **kwargs) user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name="owner") name = models.CharField(max_length=100, db_index=True) description = models.CharField(max_length=1000, db_index=True) event = models.CharField(max_length=100, db_index=True, default="", blank=True) region = models.ForeignKey(Region, null=True, blank=True, on_delete=models.CASCADE) preset = models.ForeignKey(DatamodelPreset, on_delete=models.CASCADE, null=True, blank=True) published = models.BooleanField(default=False, db_index=True) # publish export visibility = models.CharField(max_length=10, choices=visibility_choices, default=VisibilityState.PRIVATE.value) featured = models.BooleanField(default=False, db_index=True) # datapack is featured the_geom = models.MultiPolygonField(verbose_name="Extent for export", srid=4326, default="") the_geom_webmercator = models.MultiPolygonField( verbose_name="Mercator extent for export", srid=3857, default="") the_geog = models.MultiPolygonField( verbose_name="Geographic extent for export", geography=True, default="") original_selection = models.GeometryCollectionField( verbose_name="The original map selection", srid=4326, default=GeometryCollection, null=True, blank=True) include_zipfile = models.BooleanField(default=False) json_tags = models.JSONField(default=dict) last_export_run = models.ForeignKey("tasks.ExportRun", on_delete=models.DO_NOTHING, null=True, related_name="last_export_run") projections = models.ManyToManyField(Projection, related_name="projections") class Meta: # pragma: no cover managed = True db_table = "jobs" def save(self, *args, **kwargs): self.the_geom = convert_polygon(self.the_geom) self.the_geog = GEOSGeometry(self.the_geom) self.the_geom_webmercator = self.the_geom.transform(ct=3857, clone=True) super(Job, self).save(*args, **kwargs) def __str__(self): return "{0}".format(self.name) @property def overpass_extents(self): """ Return the export extents in order required by Overpass API. """ extents = GEOSGeometry(self.the_geom).extent # (w,s,e,n) # overpass needs extents in order (s,w,n,e) overpass_extents = "{0},{1},{2},{3}".format(str(extents[1]), str(extents[0]), str(extents[3]), str(extents[2])) return overpass_extents @property def extents(self): return GEOSGeometry(self.the_geom).extent # (w,s,e,n) @property def filters(self): """ Return key=value pairs for each tag in this export. Used in utils.overpass.filter to filter the export. """ # Command-line key=value filters for osmfilter filters = [] for tag in self.json_tags: kv = "{0}={1}".format(tag["key"], tag["value"]) filters.append(kv) return filters @property def categorised_tags(self): """ Return tags mapped according to their geometry types. """ points = set() lines = set() polygons = set() for tag in self.json_tags: if "point" in tag["geom"]: points.add(tag["key"]) if "line" in tag["geom"]: lines.add(tag["key"]) if "polygon" in tag["geom"]: polygons.add(tag["key"]) return { "points": sorted(list(points)), "lines": sorted(list(lines)), "polygons": sorted(list(polygons)), } @property def bounds_geojson(self): return serialize("geojson", [self], geometry_field="the_geom", fields=("name", "the_geom")) @property def attribute_classes(self): providers = [ provider_task.provider for provider_task in self.data_provider_tasks.all() ] return AttributeClass.objects.filter( data_providers__in=providers).distinct() def get_last_run(self): return self.runs.last()
class GeographicRegion(TranslatableModel, models.Model, ContentFetchingMixin): """Common model to represent levels 1, 2, 3""" __translatable__ = { "title": lambda l: models.CharField( _("Title in {LANGUAGE_NAME}".format(**l)), max_length=256, default='', blank=True, ), } level = models.IntegerField( choices=[ (1, _('Country')), (2, _('Region')), (3, _('City')), ] ) geom = models.MultiPolygonField(srid=4326,blank=True, null=True,) parent = models.ForeignKey('self', related_name='children', null=True, blank=True) name = models.CharField(max_length=256, default='') slug = models.CharField(max_length=100, default='') code = models.CharField(max_length=16, blank=True) hidden = models.BooleanField(default=False) languages_available = models.CharField( max_length=300, blank=True, help_text=_('Comma separated values of languages available in this region') ) restrict_access_to = models.TextField( null=True, blank=True, help_text=_('Comma separated values of code of siblings visible from this region') ) site = models.ForeignKey(Site, related_name='+', null=True, blank=True) objects = models.GeoManager() def __str__(self): return "%s %s" % (self.get_level_display(), self.name) @property def centroid(self): return self.geom.centroid @property def depth(self): return len(list(self.parents)) @property def parents(self): me = self while me.parent: me = me.parent yield me @property def important_information(self): pages = [{ "id": p.page.id, "slug": p.page.slug, "code": p.page.slug, "title": p.page.title, "name": p.page.title, "hidden": False, "metadata": {"page_title": p.page.title,}, "content": [{ "vector_icon": p.page.icon, "hide_from_toc": p.page.pop_up, "section": p.page.html(), "metadata": {}, "title": p.page.title, "important": False, "anchor_name": p.page.slug, "index": i, "inherited": False, }] + [{ "vector_icon": "", "hide_from_toc": True, "section": sp['html'], "metadata": { "page_title": sp['title'] }, "title": sp['title'], "important": False, "anchor_name": sp['slug'], "index": z, "inherited": False, } for z, sp in enumerate(p.page.get_sub_sections()) ] } for i, p in enumerate(self.pages_with_order.filter(page__important=True).order_by('index'))] return pages @property def full_slug(self): return "--".join(reversed([self.slug] + [p.slug for p in self.parents])) @property def uri(self): return "/".join(reversed([self.slug] + [p.slug for p in self.parents if p.level != 2])) + '/' def get_all_languages(self): return set([]) def get_sections(self, language='en', environment='production'): pages = [{ "vector_icon": p.page.icon, "hide_from_toc": p.page.pop_up, "section": p.page.html(language), "metadata": { "page_title": p.page.title }, "title": p.page.title, "important": False, "anchor_name": p.page.slug, "index": i, "inherited": False, } for i, p in enumerate( self.pages_with_order.filter(page__important=False, page__banner=False, page__status=environment).order_by( 'index'))] page_like_objects = [ [ { "vector_icon": "", "hide_from_toc": True, "section": sp['html'], "metadata": { "page_title": sp['title'] }, "title": sp['title'], "important": False, "anchor_name": sp['slug'], "index": i, "inherited": False, } for i, sp in enumerate(p.page.get_sub_sections(language)) ] for p in self.pages_with_order.filter(page__important=False, page__banner=False, page__status=environment) ] return pages + list(itertools.chain.from_iterable(page_like_objects)) def get_sub_pages(self, environment='production'): pages = [{ "id": p.page.id, "slug": p.page.slug, "code": p.page.slug, "title": p.page.title, "name": p.page.title, "hidden": False, "metadata": {"page_title": p.page.title,}, "content": [{ "vector_icon": p.page.icon, "hide_from_toc": p.page.pop_up, "section": p.page.html(), "metadata": {}, "title": p.page.title, "important": False, "anchor_name": p.page.slug, "index": i, "inherited": False, }] + [{ "vector_icon": "", "hide_from_toc": True, "section": sp['html'], "metadata": { "page_title": sp['title'] }, "title": sp['title'], "important": False, "anchor_name": sp['slug'], "index": z, "inherited": False, } for z, sp in enumerate(p.page.get_sub_sections()) ] } for i, p in enumerate( self.pages_with_order.filter(page__important=True, page__banner=False, page__status=environment).order_by( 'index'))] return pages def metadata(self, language='en', environment='production'): banners = self.pages_with_order.filter(page__banner=True, page__status=environment) return { "banners": [p.page.html() for p in banners], "page_title": self.title } def get_all_children(self): return GeographicRegion.objects.filter(Q(parent=self) | Q(parent__parent=self) | Q(id=self.id)) class Meta: ordering = ['level', 'name']
class Project(models.Model): TR55 = 'tr-55' GWLFE = 'gwlfe' MODEL_PACKAGES = ( (TR55, 'Site Storm Model'), (GWLFE, 'Watershed Multi-Year Model'), ) user = models.ForeignKey(User, on_delete=models.PROTECT) name = models.CharField( max_length=255) area_of_interest = models.MultiPolygonField( null=True, help_text='Base geometry for all scenarios of project') area_of_interest_name = models.CharField( null=True, max_length=255, help_text='A human name for the area of interest') is_private = models.BooleanField( default=True) model_package = models.CharField( choices=MODEL_PACKAGES, max_length=255, help_text='Which model pack was chosen for this project') created_at = models.DateTimeField( auto_now=False, auto_now_add=True) modified_at = models.DateTimeField( auto_now=True) is_activity = models.BooleanField( default=False, help_text='Projects with special properties') gis_data = models.TextField( null=True, help_text='Serialized JSON representation of additional' ' data gathering steps, such as MapShed.') mapshed_job_uuid = models.ForeignKey( Job, to_field='uuid', related_name='mapshed_job', on_delete=models.SET_NULL, null=True, help_text='The job used to calculate the MapShed results.' ' Used for getting the results of that job.') subbasin_mapshed_job_uuid = models.ForeignKey( Job, to_field='uuid', related_name='subbasin_mapshed_job', on_delete=models.SET_NULL, null=True, help_text='The job used to calculate the MapShed results' ' for each HUC-12 sub-basin of the shape.') wkaoi = models.CharField( null=True, max_length=255, help_text='Well-Known Area of Interest ID for faster geoprocessing') layer_overrides = JSONField( default=dict, help_text='JSON object of layers to override defaults with') def __unicode__(self): return self.name @property def in_drb(self): return self.area_of_interest.within(settings.DRB_SIMPLE_PERIMETER)
class Region(models.Model): name = models.CharField(max_length=50) the_geom = models.MultiPolygonField()
class BaseMap(models.Model): name = models.CharField(max_length=50) geometry = models.MultiPolygonField(srid=4326) def __str__(self): return self.name
class Parcel(models.Model): short_pin = models.CharField(max_length=100, blank=True, null=True) zoning = models.CharField(max_length=100, blank=True, null=True) pre_percent = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) pre_value = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) eq_value = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) tent_value = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) acreage_of_parent = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) assessed_value = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) building_assessment = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) capped_value = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) cvt_code = models.CharField(max_length=10, blank=True, null=True) cvt_description = models.CharField(max_length=40, blank=True, null=True) historical_district = models.CharField(max_length=4, blank=True, null=True) homestead_taxable = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) homestead_percent = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) lastupdate = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) legal_description = models.TextField(blank=True, null=True) owner_care_of = models.CharField(max_length=100, blank=True, null=True) owner_city = models.CharField(max_length=100, blank=True, null=True) owner_country = models.CharField(max_length=100, blank=True, null=True) owner_name = models.CharField(max_length=100, blank=True, null=True) owner_name2 = models.CharField(max_length=100, blank=True, null=True) owner_state = models.CharField(max_length=2, blank=True, null=True) owner_street = models.CharField(max_length=100, blank=True, null=True) owner_zip = models.CharField(max_length=20, blank=True, null=True) parent_parcel_num = models.CharField(max_length=40, blank=True, null=True) pin = models.CharField(max_length=40, blank=True, null=True) prop_city = models.CharField(max_length=100, blank=True, null=True) prop_class = models.CharField(max_length=10, blank=True, null=True) prop_class_description = models.CharField(max_length=100, blank=True, null=True) prop_state = models.CharField(max_length=2, blank=True, null=True) prop_street = models.CharField(max_length=100, blank=True, null=True) prop_street_num = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) prop_zip = models.CharField(max_length=20, blank=True, null=True) school_district = models.CharField(max_length=100, blank=True, null=True) school_name = models.CharField(max_length=100, blank=True, null=True) sev = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) shape_area = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) shape_len = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) stated_area = models.CharField(max_length=20, blank=True, null=True) status_code = models.CharField(max_length=4, blank=True, null=True) status_desc = models.CharField(max_length=20, blank=True, null=True) taxable_value = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) txpyrs_care_of = models.CharField(max_length=100, blank=True, null=True) txpyrs_city = models.CharField(max_length=100, blank=True, null=True) txpyrs_country = models.CharField(max_length=100, blank=True, null=True) txpyrs_name = models.CharField(max_length=100, blank=True, null=True) txpyrs_state = models.CharField(max_length=100, blank=True, null=True) txpyrs_street_addr = models.CharField(max_length=100, blank=True, null=True) txpyrs_zip_code = models.CharField(max_length=20, blank=True, null=True) unit_apt_num = models.CharField(max_length=20, blank=True, null=True) geometry = models.TextField(blank=True, null=True) geom = models.MultiPolygonField(srid=4326, blank=True, null=True) centroid = models.PointField() lon_centroid = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) lat_centroid = models.DecimalField(max_digits=65535, decimal_places=65535, blank=True, null=True) county_gid = models.IntegerField(blank=True, null=True) cd_gid = models.IntegerField(blank=True, null=True) ct_gid = models.IntegerField(blank=True, null=True) class Meta: managed = False db_table = 'parcel' def __str__(self): return '%s' % self.pin
class Park(models.Model): """ Park or similar Open Space. """ ACCESS_CHOICES = ( ('y', 'Yes'), ('n', 'No'), ('u', 'Unknown'), ) os_id = models.CharField('OS ID', max_length=9, null=True, blank=True, help_text='Refers to MassGIS OS_ID') name = models.CharField(max_length=100, blank=True, null=True) slug = models.SlugField(max_length=100, blank=True, null=True, unique=True) alt_name = models.CharField('Alternative name', max_length=100, blank=True, null=True) description = models.TextField(blank=True, null=True) address = models.CharField(max_length=50, blank=True, null=True) phone = models.CharField(max_length=50, blank=True, null=True) neighborhoods = models.ManyToManyField(Neighborhood, related_name='neighborhoods', blank=True) parktype = models.ForeignKey(Parktype, blank=True, null=True) parkowner = models.ForeignKey(Parkowner, blank=True, null=True) friendsgroup = models.ForeignKey("Friendsgroup", blank=True, null=True) events = models.ManyToManyField("Event", related_name="events", blank=True, null=True) access = models.CharField(max_length=1, blank=True, null=True, choices=ACCESS_CHOICES) area = models.FloatField(blank=True, null=True) image = models.ImageField(blank=True, upload_to="parkimages") def parkimage_thumb(self): if self.image: thumb = get_thumbnail(self.image.file, settings.ADMIN_THUMBS_SIZE, crop='center', quality=80) return u'<img width="%s" height="%s" src="%s" />' % ( thumb.width, thumb.height, thumb.url) else: return None geometry = models.MultiPolygonField(srid=26986) objects = models.GeoManager() class Meta: verbose_name = _('Park') verbose_name_plural = _('Parks') ordering = ['name'] def __unicode__(self): return self.name @models.permalink def get_absolute_url(self): return ('park', [slugify(self.name)]) def area_acres(self): return self.area / 4047 def lat_long(self): self.geometry.transform(4326) return [self.geometry.centroid.y, self.geometry.centroid.x] def save(self, *args, **kwargs): self.area = self.geometry.area self.slug = slugify(self.name) super(Park, self).save(*args, **kwargs) try: # cache containing neighorhood # doesn't work with admin forms, m2m get cleared during admin save neighborhoods = Neighborhood.objects.filter( geometry__intersects=self.geometry) self.neighborhoods.clear() self.neighborhoods.add(*neighborhoods) except TypeError: self.neighborhoods = None
class JburgLanduse(models.Model): cartotypen = models.CharField(null=True, blank=True, max_length=255) geom = models.MultiPolygonField(srid=4326) def __str__(self): return self.cartotypen
class Project(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('user'), related_name='projects', null=True, blank=True, on_delete=models.PROTECT) created_at = models.DateTimeField(verbose_name=_('created at'), auto_now_add=True, editable=False) modified_at = models.DateTimeField(verbose_name=_('modified at'), auto_now=True, editable=False) name = models.CharField(max_length=255, verbose_name=_('name')) identifier = models.CharField(max_length=50, verbose_name=_('identifier'), db_index=True, blank=True, null=True) type = models.ForeignKey(ProjectType, verbose_name=_('type'), related_name='projects', on_delete=models.PROTECT) attribute_data = JSONField(verbose_name=_('attribute data'), default=dict, blank=True, null=True, encoder=DjangoJSONEncoder) phase = models.ForeignKey('ProjectPhase', verbose_name=_('phase'), null=True, related_name='projects', on_delete=models.PROTECT) geometry = models.MultiPolygonField(null=True, blank=True) class Meta: verbose_name = _('project') verbose_name_plural = _('projects') ordering = ('name', ) def __str__(self): return self.name def get_attribute_data(self): ret = {} for attribute in Attribute.objects.all().prefetch_related( 'value_choices'): deserialized_value = None if attribute.value_type == Attribute.TYPE_GEOMETRY: deserialized_value = self.geometry elif attribute.value_type == Attribute.TYPE_IMAGE: try: deserialized_value = ProjectAttributeImage.objects.get( attribute=attribute, project=self, ).image except ProjectAttributeImage.DoesNotExist: deserialized_value = None elif attribute.identifier in self.attribute_data: deserialized_value = attribute.deserialize_value( self.attribute_data[attribute.identifier]) ret[attribute.identifier] = deserialized_value return ret def set_attribute_data(self, data): self.attribute_data = {} self.geometry = None self.update_attribute_data(data) def update_attribute_data(self, data): attributes = { a.identifier: a for a in Attribute.objects.all().prefetch_related('value_choices') } for identifier, value in data.items(): attribute = attributes.get(identifier) if not attribute: continue if attribute.value_type == Attribute.TYPE_GEOMETRY: self.geometry = value elif attribute.value_type == Attribute.TYPE_IMAGE: if value is False: ProjectAttributeImage.objects.filter( attribute=attribute, project=self).delete() elif value is None: # None is handled in the same way as omitting this attribute from the update in the first place # would have been, ie. do nothing. This is to make life easier as the form where these images # mainly come from uses False for "delete" and None for "no update". continue else: ProjectAttributeImage.objects.update_or_create( attribute=attribute, project=self, defaults={'image': value}) else: serialized_value = attribute.serialize_value(value) if serialized_value is not None: self.attribute_data[identifier] = serialized_value else: self.attribute_data.pop(identifier, None) def get_time_line(self): timeline = [{ 'content': 'Luontipvm', 'start': self.created_at, 'group': self.id, 'type': 'point', }] for log_entry in self.phase_logs.order_by('created_at'): timeline.append({ # 'title': None, 'content': log_entry.phase.name, 'start': log_entry.created_at, 'end': None, 'group': self.id, 'type': 'background', 'className': log_entry.phase.color, }) if timeline[-2]['type'] == 'background': timeline[-2]['end'] = log_entry.created_at if timeline[-1]['type'] == 'background' and not timeline[-1]['end']: timeline[-1]['end'] = timezone.now() for attribute in Attribute.objects.filter( value_type=Attribute.TYPE_DATE): if attribute.identifier in self.attribute_data and self.attribute_data[ attribute.identifier]: start_dt = attribute.deserialize_value( self.attribute_data[attribute.identifier]) timeline.append({ 'type': 'point', 'content': attribute.name, 'start': start_dt, 'group': self.id, }) # TODO: Remove hard-coded logic if attribute.identifier == 'oas_aineiston_esillaoloaika_alkaa': timeline.append({ 'type': 'point', 'content': 'OAS-paketin määräaika', 'start': start_dt - datetime.timedelta(weeks=2), 'group': self.id, }) if attribute.identifier == 'ehdotuksen_suunniteltu_lautakuntapaivamaara_arvio' and \ 'prosessin_kokoluokka' in self.attribute_data: weeks = 6 if self.attribute_data[ 'prosessin_kokoluokka'] in ['l', 'xl'] else 14 timeline.append({ 'type': 'point', 'content': 'Lautakuntapaketin määräaika', 'start': start_dt - datetime.timedelta(weeks=weeks), 'group': self.id, }) return timeline
class AOI(GeoQBase, Assignment): """ Low-level organizational object. Now (6/1/14) referred to as a 'Workcell' """ STATUS_VALUES = STATUS_VALUES_LIST STATUS_CHOICES = [(choice, choice) for choice in STATUS_VALUES] PRIORITIES = [(n, n) for n in range(1, 6)] analyst = models.ForeignKey( User, blank=True, null=True, help_text="User assigned to work the workcell.") job = models.ForeignKey(Job, related_name="aois") reviewers = models.ManyToManyField( User, blank=True, null=True, related_name="aoi_reviewers", help_text='Users that actually reviewed this work.') objects = AOIManager() polygon = models.MultiPolygonField() priority = models.SmallIntegerField(choices=PRIORITIES, max_length=1, default=5) status = models.CharField(max_length=15, choices=STATUS_CHOICES, default='Unassigned') class Meta: permissions = ( ('assign_workcells', 'Assign Workcells'), ('certify_workcells', 'Certify Workcells'), ) def __unicode__(self): aoi_obj = '%s - AOI %s' % (self.name, self.id) return aoi_obj @property def log(self): return Comment.objects.filter(aoi=self).order_by('created_at') @property def assignee_name(self): if self.assignee_id is None: return 'Unknown' else: if self.assignee_type_id == AssigneeType.USER: return User.objects.get(id=self.assignee_id).username else: return Group.objects.get(id=self.assignee_id).name #def save(self): # if analyst or reviewer updated, then create policy to give them permission to edit this object..... # -- Afterwards -- check how this will work with the views. def get_absolute_url(self): if self.job.editable_layer_id is None: return reverse('aoi-work', args=[self.id]) else: return reverse('aoi-mapedit', args=[self.id]) def geoJSON(self): """ Returns geoJSON of the feature. """ if self.id is None: self.id = 1 geojson = SortedDict() geojson["type"] = "Feature" geojson["properties"] = dict( id=self.id, status=self.status, analyst=(self.analyst.username if self.analyst is not None else 'None'), assignee=self.assignee_name, priority=self.priority, delete_url=reverse('aoi-deleter', args=[self.id])) geojson["geometry"] = json.loads(self.polygon.json) geojson["properties"]["absolute_url"] = self.get_absolute_url() return clean_dumps(geojson) def logJSON(self): return [ob.to_dict() for ob in self.log] def properties_json(self): """ Returns json of the feature properties. """ if self.id is None: self.id = 1 properties_main = self.properties or {} properties_built = dict(status=self.status, analyst=(self.analyst.username if self.analyst is not None else 'Unassigned'), priority=self.priority) prop_json = dict(properties_built.items() + properties_main.items()) return clean_dumps(prop_json) def map_detail(self): """ Get map coordinates for MapEdit """ center = self.polygon.centroid return "15/%f/%f" % (center.y, center.x) def grid_geoJSON(self): """ Return geoJSON of workcells for export """ if self.id is None: self.id = 1 geojson = SortedDict() geojson["type"] = "Feature" geojson["properties"] = dict(id=self.id, priority=self.priority, status=self.status) geojson["geometry"] = json.loads(self.polygon.json) return clean_dumps(geojson) def user_can_complete(self, user): """ Returns whether the user can update the AOI as complete. """ return user == self.analyst or user in self.job.reviewers.all() class Meta: verbose_name = 'Area of Interest' verbose_name_plural = 'Areas of Interest'