summaryrefslogtreecommitdiff
path: root/ishtar_common/models_common.py
diff options
context:
space:
mode:
Diffstat (limited to 'ishtar_common/models_common.py')
-rw-r--r--ishtar_common/models_common.py671
1 files changed, 346 insertions, 325 deletions
diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py
index ebf480ff0..cc3c61b05 100644
--- a/ishtar_common/models_common.py
+++ b/ishtar_common/models_common.py
@@ -1985,12 +1985,356 @@ class Canton(models.Model):
return settings.JOINT.join((self.name, str(self.arrondissement)))
+class SpatialReferenceSystem(GeneralType):
+ order = models.IntegerField(_("Order"), default=10)
+ auth_name = models.CharField(_("Authority name"), default="EPSG", max_length=256)
+ srid = models.IntegerField(_("Authority SRID"))
+
+ class Meta:
+ verbose_name = _("Spatial reference system")
+ verbose_name_plural = _("Spatial reference systems")
+ ordering = (
+ "order",
+ "label",
+ )
+
+ @classmethod
+ def get_documentation_string(cls):
+ """
+ Used for automatic documentation generation
+ """
+ doc = super(SpatialReferenceSystem, cls).get_documentation_string()
+ doc += ", **srid** {}, **auth_name** {}".format(
+ _("Authority SRID"), _("Authority name")
+ )
+ return doc
+
+
+post_save.connect(post_save_cache, sender=SpatialReferenceSystem)
+post_delete.connect(post_save_cache, sender=SpatialReferenceSystem)
+
+
+class GeoOriginType(HierarchicalType):
+ """
+ ex: topographical surveys, georeferencing, ...
+ """
+
+ order = models.IntegerField(_("Order"), default=10)
+
+ class Meta:
+ verbose_name = _("Geographic - Origin type")
+ verbose_name_plural = _("Geographic - Origin types")
+ ordering = (
+ "order",
+ "label",
+ )
+
+
+class GeoDataType(HierarchicalType):
+ """
+ ex: outline, z-sup, ...
+ """
+
+ order = models.IntegerField(_("Order"), default=10)
+
+ class Meta:
+ verbose_name = _("Geographic - Data type")
+ verbose_name_plural = _("Geographic - Data types")
+ ordering = (
+ "order",
+ "label",
+ )
+
+
+class GeoProviderType(HierarchicalType):
+ """
+ ex: GeoNames, IGN, ...
+ """
+
+ order = models.IntegerField(_("Order"), default=10)
+
+ class Meta:
+ verbose_name = _("Geographic - Provider type")
+ verbose_name_plural = _("Geographic - Provider types")
+ ordering = (
+ "order",
+ "label",
+ )
+
+
+class GeoVectorData(models.Model):
+ name = models.CharField(_("Name"), default=_("Default"), max_length=200)
+ source_content_type = models.ForeignKey(
+ ContentType, related_name="content_type_geovectordata", on_delete=models.CASCADE
+ )
+ source_id = models.PositiveIntegerField()
+ source = GenericForeignKey("source_content_type", "source_id")
+ origin = models.ForeignKey(
+ GeoOriginType,
+ blank=True,
+ null=True,
+ on_delete=models.PROTECT,
+ verbose_name=_("Origin"),
+ )
+ data_type = models.ForeignKey(
+ GeoDataType,
+ blank=True,
+ null=True,
+ on_delete=models.PROTECT,
+ verbose_name=_("Data type"),
+ )
+ provider = models.ForeignKey(
+ GeoProviderType,
+ blank=True,
+ null=True,
+ on_delete=models.PROTECT,
+ verbose_name=_("Provider"),
+ )
+ comment = models.TextField(_("Comment"), default="", blank=True)
+ x = models.FloatField(_("X"), blank=True, null=True, help_text=_("User input"))
+ y = models.FloatField(_("Y"), blank=True, null=True, help_text=_("User input"))
+ z = models.FloatField(_("Z"), blank=True, null=True, help_text=_("User input"))
+ # x == cached_x if user input else get it from other sources
+ # cached is converted to the display SRID
+ cached_x = models.FloatField(_("X (cached)"), blank=True, null=True)
+ cached_y = models.FloatField(_("Y (cached)"), blank=True, null=True)
+ cached_z = models.FloatField(_("Z (cached)"), blank=True, null=True)
+ estimated_error_x = models.FloatField(
+ _("Estimated error for X"), blank=True, null=True
+ )
+ estimated_error_y = models.FloatField(
+ _("Estimated error for Y"), blank=True, null=True
+ )
+ estimated_error_z = models.FloatField(
+ _("Estimated error for Z"), blank=True, null=True
+ )
+ spatial_reference_system = models.ForeignKey(
+ SpatialReferenceSystem,
+ verbose_name=_("Spatial Reference System"),
+ blank=True,
+ null=True,
+ on_delete=models.PROTECT,
+ )
+ point_2d = models.PointField(_("Point (2D)"), blank=True, null=True)
+ point_3d = models.PointField(_("Point (3D)"), blank=True, null=True, dim=3)
+ multi_points = models.MultiPointField(_("Multi points"), blank=True, null=True)
+ multi_line = models.MultiLineStringField(_("Multi line"), blank=True, null=True)
+ multi_polygon = models.MultiPolygonField(_("Multi polygon"), blank=True, null=True)
+ need_update = models.BooleanField(_("Need update"), default=False)
+
+ class Meta:
+ verbose_name = _("Geographic - Vector data")
+ verbose_name_plural = _("Geographic - Vector data")
+
+ def __str__(self):
+ name = self.name
+ if self.data_type:
+ name += f" ({str(self.data_type).lower()})"
+ return name
+
+ def display_coordinates(self, rounded=5, dim=2, cache=True):
+ srid = None
+ profile = get_current_profile()
+ if profile.display_srs and profile.display_srs.srid:
+ srid = profile.display_srs.srid
+ return self.get_coordinates(rounded=rounded, srid=srid, dim=dim, cache=cache)
+
+ def get_coordinates(self, rounded=5, srid: int = None, dim=2, cache=False):
+ if dim not in (2, 3):
+ raise ValueError(_("Only 2 or 3 dimension"))
+ if cache:
+ coordinates = [self.cached_x, self.cached_y]
+ if dim == 3:
+ coordinates.append(self.cached_z)
+ else:
+ if self.x and self.y: # user input
+ if not srid or srid == self.spatial_reference_system.srid:
+ coordinates = [self.x, self.y]
+ if dim == 3:
+ coordinates.append(self.z)
+ else:
+ args = {
+ "x": self.x,
+ "y": self.y,
+ "srid": self.spatial_reference_system.srid,
+ }
+ if dim == 3:
+ args["z"] = self.z
+ point = Point(**args).transform(srid, clone=True)
+ coordinates = [point.x, point.y]
+ if dim == 3:
+ coordinates.append(point.z)
+ elif self.point_2d and dim == 2:
+ point = self.point_2d.transform(srid, clone=True)
+ coordinates = [point.x, point.y]
+ elif self.point_3d and dim == 3:
+ point = self.point_3d.transform(srid, clone=True)
+ coordinates = [point.x, point.y, point.z]
+ else:
+ return
+ if not rounded:
+ return coordinates
+ return [round(coord, rounded) for coord in coordinates]
+
+ def get_coordinates_from_polygon(self, rounded=5, srid: int = None):
+ if self.multi_polygon:
+ return self.convert_coordinates(
+ self.multi_polygon.centroid, rounded=rounded, srid=srid
+ )
+
+ def get_x(self, srid: int = None) -> float:
+ coord = self.get_coordinates(srid)
+ if coord:
+ return coord[0]
+
+ def get_y(self, srid: int = None) -> float:
+ coord = self.get_coordinates(srid)
+ if coord:
+ return coord[1]
+
+ def get_z(self, srid: int = None) -> float:
+ coord = self.get_coordinates(srid, dim=3)
+ if coord:
+ return coord[2]
+
+ @property
+ def display_spatial_reference_system(self):
+ profile = get_current_profile()
+ if not profile.display_srs or not profile.display_srs.srid:
+ return self.spatial_reference_system
+ return profile.display_srs
+
+ def get_geo_items(self, get_polygons, rounded=5):
+ label = self.label if hasattr(self, "label") else self.short_label
+ dct = {"type": "Feature", "geometry": {}, "properties": {"label": label}}
+ if get_polygons:
+ list_coords = []
+ if self.multi_polygon:
+ for polygon in self.multi_polygon:
+ list_coords.append([])
+ for linear_ring in range(len(polygon)):
+ list_coords[-1].append([])
+ for coords in polygon[linear_ring].coords:
+ point_2d = Point(
+ coords[0], coords[1], srid=self.multi_polygon.srid
+ )
+ list_coords[-1][linear_ring].append(
+ self.convert_coordinates(point_2d, rounded)
+ )
+ dct["geometry"]["type"] = "MultiPolygon"
+ dct["geometry"]["coordinates"] = list_coords
+ else:
+ dct["geometry"]["type"] = "Point"
+ coords = self.display_coordinates()
+ if coords:
+ dct["geometry"]["coordinates"] = coords
+ elif self.multi_polygon:
+ dct["geometry"]["coordinates"] = self.convert_coordinates(
+ self.multi_polygon.centroid, rounded
+ )
+ else:
+ return {}
+ return dct
+
+ def convert_coordinates(self, point_2d, rounded=5, srid=None):
+ if not srid:
+ profile = get_current_profile()
+ if profile.display_srs and profile.display_srs.srid:
+ srid = profile.display_srs.srid
+ if not srid:
+ x, y = point_2d.x, point_2d.y
+ else:
+ point = point_2d.transform(srid, clone=True)
+ x, y = point.x, point.y
+ if rounded:
+ return [round(x, rounded), round(y, rounded)]
+ return [x, y]
+
+ def most_precise_geo(self):
+ if self.multi_polygon:
+ return "multi_polygon"
+ if self.point_2d:
+ return "point"
+
+ def _geojson_serialize(self, geom_attr):
+ if not hasattr(self, geom_attr):
+ return ""
+ geojson = serialize(
+ "geojson",
+ self.__class__.objects.filter(pk=self.pk),
+ geometry_field=geom_attr,
+ fields=("name",),
+ )
+ geojson_dct = json.loads(geojson)
+ profile = get_current_profile()
+ precision = profile.point_precision
+
+ features = geojson_dct.pop("features")
+ for idx in range(len(features)):
+ feature = features[idx]
+ lbl = feature["properties"].pop("name")
+ feature["properties"]["name"] = lbl
+ feature["properties"]["id"] = self.pk
+ if precision is not None:
+ geom_type = feature["geometry"].get("type", None)
+ if geom_type == "Point":
+ feature["geometry"]["coordinates"] = [
+ round(coord, precision)
+ for coord in feature["geometry"]["coordinates"]
+ ]
+ geojson_dct["features"] = features
+ geojson_dct["link_template"] = simple_link_to_window(self).replace(
+ "999999", "<pk>"
+ )
+ geojson = json.dumps(geojson_dct)
+ return geojson
+
+ @property
+ def point_2d_geojson(self):
+ return self._geojson_serialize("point_2d")
+
+ @property
+ def multi_polygon_geojson(self):
+ return self._geojson_serialize("multi_polygon")
+
+
+post_save.connect(post_save_geodata, sender=GeoVectorData)
+
+
+class GeographicItem(models.Model):
+ main_geodata = models.ForeignKey(
+ GeoVectorData,
+ on_delete=models.SET_NULL,
+ blank=True,
+ null=True,
+ related_name="main_related_items_%(app_label)s_%(class)s",
+ )
+ geodata = models.ManyToManyField(
+ GeoVectorData, blank=True, related_name="related_items_%(app_label)s_%(class)s"
+ )
+
+ class Meta:
+ abstract = True
+
+ def save(
+ self, force_insert=False, force_update=False, using=None, update_fields=None
+ ):
+ super(GeographicItem, self).save(
+ force_insert=force_insert,
+ force_update=force_update,
+ using=using,
+ update_fields=update_fields,
+ )
+ if self.main_geodata and not self.geodata.filter(pk=self.main_geodata.pk):
+ self.geodata.add(self.main_geodata)
+
+
class TownManager(models.Manager):
def get_by_natural_key(self, numero_insee, year):
return self.get(numero_insee=numero_insee, year=year)
-class Town(Imported, models.Model):
+class Town(GeographicItem, Imported, models.Model):
name = models.CharField(_("Name"), max_length=100)
surface = models.IntegerField(_("Surface (m2)"), blank=True, null=True)
center = models.PointField(
@@ -2868,330 +3212,7 @@ class DynamicRequest:
return alt_names
-class SpatialReferenceSystem(GeneralType):
- order = models.IntegerField(_("Order"), default=10)
- auth_name = models.CharField(_("Authority name"), default="EPSG", max_length=256)
- srid = models.IntegerField(_("Authority SRID"))
-
- class Meta:
- verbose_name = _("Spatial reference system")
- verbose_name_plural = _("Spatial reference systems")
- ordering = (
- "order",
- "label",
- )
-
- @classmethod
- def get_documentation_string(cls):
- """
- Used for automatic documentation generation
- """
- doc = super(SpatialReferenceSystem, cls).get_documentation_string()
- doc += ", **srid** {}, **auth_name** {}".format(
- _("Authority SRID"), _("Authority name")
- )
- return doc
-
-
-post_save.connect(post_save_cache, sender=SpatialReferenceSystem)
-post_delete.connect(post_save_cache, sender=SpatialReferenceSystem)
-
-
-class GeoOriginType(HierarchicalType):
- """
- ex: topographical surveys, georeferencing, ...
- """
-
- order = models.IntegerField(_("Order"), default=10)
-
- class Meta:
- verbose_name = _("Geographic - Origin type")
- verbose_name_plural = _("Geographic - Origin types")
- ordering = (
- "order",
- "label",
- )
-
-
-class GeoDataType(HierarchicalType):
- """
- ex: outline, z-sup, ...
- """
-
- order = models.IntegerField(_("Order"), default=10)
-
- class Meta:
- verbose_name = _("Geographic - Data type")
- verbose_name_plural = _("Geographic - Data types")
- ordering = (
- "order",
- "label",
- )
-
-
-class GeoProviderType(HierarchicalType):
- """
- ex: GeoNames, IGN, ...
- """
-
- order = models.IntegerField(_("Order"), default=10)
-
- class Meta:
- verbose_name = _("Geographic - Provider type")
- verbose_name_plural = _("Geographic - Provider types")
- ordering = (
- "order",
- "label",
- )
-
-
-class GeoVectorData(models.Model):
- name = models.CharField(_("Name"), default=_("Default"), max_length=200)
- source_content_type = models.ForeignKey(
- ContentType, related_name="content_type_geovectordata", on_delete=models.CASCADE
- )
- source_id = models.PositiveIntegerField()
- source = GenericForeignKey("source_content_type", "source_id")
- origin = models.ForeignKey(
- GeoOriginType,
- blank=True,
- null=True,
- on_delete=models.PROTECT,
- verbose_name=_("Origin"),
- )
- data_type = models.ForeignKey(
- GeoDataType,
- blank=True,
- null=True,
- on_delete=models.PROTECT,
- verbose_name=_("Data type"),
- )
- provider = models.ForeignKey(
- GeoProviderType,
- blank=True,
- null=True,
- on_delete=models.PROTECT,
- verbose_name=_("Provider"),
- )
- comment = models.TextField(_("Comment"), default="", blank=True)
- x = models.FloatField(_("X"), blank=True, null=True, help_text=_("User input"))
- y = models.FloatField(_("Y"), blank=True, null=True, help_text=_("User input"))
- z = models.FloatField(_("Z"), blank=True, null=True, help_text=_("User input"))
- # x == cached_x if user input else get it from other sources
- # cached is converted to the display SRID
- cached_x = models.FloatField(_("X (cached)"), blank=True, null=True)
- cached_y = models.FloatField(_("Y (cached)"), blank=True, null=True)
- cached_z = models.FloatField(_("Z (cached)"), blank=True, null=True)
- estimated_error_x = models.FloatField(
- _("Estimated error for X"), blank=True, null=True
- )
- estimated_error_y = models.FloatField(
- _("Estimated error for Y"), blank=True, null=True
- )
- estimated_error_z = models.FloatField(
- _("Estimated error for Z"), blank=True, null=True
- )
- spatial_reference_system = models.ForeignKey(
- SpatialReferenceSystem,
- verbose_name=_("Spatial Reference System"),
- blank=True,
- null=True,
- on_delete=models.PROTECT,
- )
- point_2d = models.PointField(_("Point (2D)"), blank=True, null=True)
- point_3d = models.PointField(_("Point (3D)"), blank=True, null=True, dim=3)
- multi_points = models.MultiPointField(_("Multi points"), blank=True, null=True)
- multi_line = models.MultiLineStringField(_("Multi line"), blank=True, null=True)
- multi_polygon = models.MultiPolygonField(_("Multi polygon"), blank=True, null=True)
- need_update = models.BooleanField(_("Need update"), default=False)
-
- class Meta:
- verbose_name = _("Geographic - Vector data")
- verbose_name_plural = _("Geographic - Vector data")
-
- def display_coordinates(self, rounded=5, dim=2, cache=True):
- srid = None
- profile = get_current_profile()
- if profile.display_srs and profile.display_srs.srid:
- srid = profile.display_srs.srid
- return self.get_coordinates(rounded=rounded, srid=srid, dim=dim, cache=cache)
-
- def get_coordinates(self, rounded=5, srid: int = None, dim=2, cache=False):
- if dim not in (2, 3):
- raise ValueError(_("Only 2 or 3 dimension"))
- if cache:
- coordinates = [self.cached_x, self.cached_y]
- if dim == 3:
- coordinates.append(self.cached_z)
- else:
- if self.x and self.y: # user input
- if not srid or srid == self.spatial_reference_system.srid:
- coordinates = [self.x, self.y]
- if dim == 3:
- coordinates.append(self.z)
- else:
- args = {
- "x": self.x,
- "y": self.y,
- "srid": self.spatial_reference_system.srid,
- }
- if dim == 3:
- args["z"] = self.z
- point = Point(**args).transform(srid, clone=True)
- coordinates = [point.x, point.y]
- if dim == 3:
- coordinates.append(point.z)
- elif self.point_2d and dim == 2:
- point = self.point_2d.transform(srid, clone=True)
- coordinates = [point.x, point.y]
- elif self.point_3d and dim == 3:
- point = self.point_3d.transform(srid, clone=True)
- coordinates = [point.x, point.y, point.z]
- else:
- return
- if not rounded:
- return coordinates
- return [round(coord, rounded) for coord in coordinates]
-
- def get_coordinates_from_polygon(self, rounded=5, srid: int = None):
- if self.multi_polygon:
- return self.convert_coordinates(
- self.multi_polygon.centroid, rounded=rounded, srid=srid
- )
-
- def get_x(self, srid: int = None) -> float:
- coord = self.get_coordinates(srid)
- if coord:
- return coord[0]
-
- def get_y(self, srid: int = None) -> float:
- coord = self.get_coordinates(srid)
- if coord:
- return coord[1]
-
- def get_z(self, srid: int = None) -> float:
- coord = self.get_coordinates(srid, dim=3)
- if coord:
- return coord[2]
-
- @property
- def display_spatial_reference_system(self):
- profile = get_current_profile()
- if not profile.display_srs or not profile.display_srs.srid:
- return self.spatial_reference_system
- return profile.display_srs
-
- def get_geo_items(self, get_polygons, rounded=5):
- label = self.label if hasattr(self, "label") else self.short_label
- dct = {"type": "Feature", "geometry": {}, "properties": {"label": label}}
- if get_polygons:
- list_coords = []
- if self.multi_polygon:
- for polygon in self.multi_polygon:
- list_coords.append([])
- for linear_ring in range(len(polygon)):
- list_coords[-1].append([])
- for coords in polygon[linear_ring].coords:
- point_2d = Point(
- coords[0], coords[1], srid=self.multi_polygon.srid
- )
- list_coords[-1][linear_ring].append(
- self.convert_coordinates(point_2d, rounded)
- )
- dct["geometry"]["type"] = "MultiPolygon"
- dct["geometry"]["coordinates"] = list_coords
- else:
- dct["geometry"]["type"] = "Point"
- coords = self.display_coordinates()
- if coords:
- dct["geometry"]["coordinates"] = coords
- elif self.multi_polygon:
- dct["geometry"]["coordinates"] = self.convert_coordinates(
- self.multi_polygon.centroid, rounded
- )
- else:
- return {}
- return dct
-
- def convert_coordinates(self, point_2d, rounded=5, srid=None):
- if not srid:
- profile = get_current_profile()
- if profile.display_srs and profile.display_srs.srid:
- srid = profile.display_srs.srid
- if not srid:
- x, y = point_2d.x, point_2d.y
- else:
- point = point_2d.transform(srid, clone=True)
- x, y = point.x, point.y
- if rounded:
- return [round(x, rounded), round(y, rounded)]
- return [x, y]
-
- def most_precise_geo(self):
- if self.multi_polygon:
- return "multi_polygon"
- if self.point_2d:
- return "point"
-
- def _geojson_serialize(self, geom_attr):
- if not hasattr(self, geom_attr):
- return ""
- geojson = serialize(
- "geojson",
- self.__class__.objects.filter(pk=self.pk),
- geometry_field=geom_attr,
- fields=("name",),
- )
- geojson_dct = json.loads(geojson)
- profile = get_current_profile()
- precision = profile.point_precision
-
- features = geojson_dct.pop("features")
- for idx in range(len(features)):
- feature = features[idx]
- lbl = feature["properties"].pop("name")
- feature["properties"]["name"] = lbl
- feature["properties"]["id"] = self.pk
- if precision is not None:
- geom_type = feature["geometry"].get("type", None)
- if geom_type == "Point":
- feature["geometry"]["coordinates"] = [
- round(coord, precision)
- for coord in feature["geometry"]["coordinates"]
- ]
- geojson_dct["features"] = features
- geojson_dct["link_template"] = simple_link_to_window(self).replace(
- "999999", "<pk>"
- )
- geojson = json.dumps(geojson_dct)
- return geojson
-
- @property
- def point_2d_geojson(self):
- return self._geojson_serialize("point_2d")
-
- @property
- def multi_polygon_geojson(self):
- return self._geojson_serialize("multi_polygon")
-
-
-
-
-post_save.connect(post_save_geodata, sender=GeoVectorData)
-
-
-class GeoItem(models.Model):
- main_geodata = models.ForeignKey(
- GeoVectorData,
- on_delete=models.SET_NULL,
- blank=True,
- null=True,
- related_name="main_related_items_%(app_label)s_%(class)s",
- )
- geodata = models.ManyToManyField(
- GeoVectorData, blank=True, related_name="related_items_%(app_label)s_%(class)s"
- )
-
+class GeoItem(GeographicItem):
GEO_SOURCE = (("T", _("Town")), ("P", _("Precise")), ("M", _("Polygon")))
# gis