示例#1
0
class Topology(AltimetryMixin, TimeStampedModel, NoDeleteMixin):
    paths = models.ManyToManyField(Path,
                                   editable=False,
                                   db_column='troncons',
                                   through='PathAggregation',
                                   verbose_name=_(u"Path"))
    offset = models.FloatField(default=0.0,
                               db_column='decallage',
                               verbose_name=_(u"Offset"))  # in SRID units
    kind = models.CharField(editable=False,
                            verbose_name=_(u"Kind"),
                            max_length=32)

    # Override default manager
    objects = NoDeleteMixin.get_manager_cls(models.GeoManager)()

    geom = models.GeometryField(editable=False,
                                srid=settings.SRID,
                                null=True,
                                blank=True,
                                spatial_index=False,
                                dim=3)

    class Meta:
        db_table = 'e_t_evenement'
        verbose_name = _(u"Topology")
        verbose_name_plural = _(u"Topologies")

    def __init__(self, *args, **kwargs):
        super(Topology, self).__init__(*args, **kwargs)
        if not self.pk:
            self.kind = self.__class__.KIND

    @classmethod
    def add_property(cls, name, func):
        if hasattr(cls, name):
            raise AttributeError("%s has already an attribute %s" %
                                 (cls, name))
        setattr(cls, name, property(func))

    @classproperty
    def KIND(cls):
        return cls._meta.object_name.upper()

    def __unicode__(self):
        return u"%s (%s)" % (_(u"Topology"), self.pk)

    def ispoint(self):
        if not self.pk and self.geom and self.geom.geom_type == 'Point':
            return True
        return all([
            a.start_position == a.end_position
            for a in self.aggregations.all()
        ])

    def geom_as_point(self):
        geom = self.geom
        assert geom, 'Topology point is None'
        if geom.geom_type != 'Point':
            logger.warning(
                "Topology has wrong geometry type : %s instead of Point" %
                geom.geom_type)
            geom = Point(geom.coords[0], srid=settings.SRID)
        return geom

    def add_path(self, path, start=0.0, end=1.0, order=0, reload=True):
        """
        Shortcut function to add paths into this topology.
        """
        from .factories import PathAggregationFactory
        aggr = PathAggregationFactory.create(topo_object=self,
                                             path=path,
                                             start_position=start,
                                             end_position=end,
                                             order=order)
        # Since a trigger modifies geom, we reload the object
        if reload:
            self.reload()
        return aggr

    @classmethod
    def overlapping(cls, topologies):
        """ Return a Topology queryset overlapping specified topologies.
        """
        return TopologyHelper.overlapping(cls, topologies)

    def mutate(self, other, delete=True):
        """
        Take alls attributes of the other topology specified and
        save them into this one. Optionnally deletes the other.
        """
        self.offset = other.offset
        self.geom = other.geom
        self.save()
        PathAggregation.objects.filter(topo_object=self).delete()
        aggrs = other.aggregations.all()
        # A point has only one aggregation, except if it is on an intersection.
        # In this case, the trigger will create them, so ignore them here.
        if other.ispoint():
            aggrs = aggrs[:1]
        for aggr in aggrs:
            self.add_path(aggr.path,
                          aggr.start_position,
                          aggr.end_position,
                          aggr.order,
                          reload=False)
        if delete:
            other.delete(force=True)  # Really delete it from database
        self.save()
        return self

    def reload(self, fromdb=None):
        """
        Reload into instance all computed attributes in triggers.
        """
        if self.pk:
            # Update computed values
            fromdb = self.__class__.objects.get(pk=self.pk)
            self.geom = fromdb.geom
            self.offset = fromdb.offset  # /!\ offset may be set by a trigger OR in
            # the django code, reload() will override
            # any unsaved value
            AltimetryMixin.reload(self, fromdb)
            TimeStampedModel.reload(self, fromdb)
            NoDeleteMixin.reload(self, fromdb)
        return self

    @debug_pg_notices
    def save(self, *args, **kwargs):
        # HACK: these fields are readonly from the Django point of view
        # but they can be changed at DB level. Since Django write all fields
        # to DB anyway, it is important to update it before writting
        if self.pk:
            tmp = self.__class__.objects.get(pk=self.pk)
            self.length = tmp.length
            # In the case of points, the geom can be set by Django. Don't override.
            if (self.ispoint() and self.geom is None) or \
               (not self.ispoint() and tmp.geom is not None):
                self.geom = tmp.geom

        if not self.kind:
            if self.KIND == "TOPOLOGYMIXIN":
                raise Exception("Cannot save abstract topologies")
            self.kind = self.__class__.KIND

        # Static value for Topology offset, if any
        shortmodelname = self._meta.object_name.lower().replace('edge', '')
        self.offset = settings.TOPOLOGY_STATIC_OFFSETS.get(
            shortmodelname, self.offset)

        # Save into db
        super(Topology, self).save(*args, **kwargs)
        self.reload()

    def serialize(self):
        return TopologyHelper.serialize(self)

    @classmethod
    def deserialize(cls, serialized):
        return TopologyHelper.deserialize(serialized)
示例#2
0
class Topology(AltimetryMixin, TimeStampedModel, NoDeleteMixin):
    paths = models.ManyToManyField(Path,
                                   editable=False,
                                   db_column='troncons',
                                   through='PathAggregation',
                                   verbose_name=_(u"Path"))
    offset = models.FloatField(default=0.0,
                               db_column='decallage',
                               verbose_name=_(u"Offset"))  # in SRID units
    kind = models.CharField(editable=False,
                            verbose_name=_(u"Kind"),
                            max_length=32)

    # Override default manager
    objects = NoDeleteMixin.get_manager_cls(models.GeoManager)()

    geom = models.GeometryField(
        editable=(not settings.TREKKING_TOPOLOGY_ENABLED),
        srid=settings.SRID,
        null=True,
        default=None,
        spatial_index=False)
    """ Fake srid attribute, that prevents transform() calls when using Django map widgets. """
    srid = settings.API_SRID

    class Meta:
        db_table = 'e_t_evenement'
        verbose_name = _(u"Topology")
        verbose_name_plural = _(u"Topologies")

    def __init__(self, *args, **kwargs):
        super(Topology, self).__init__(*args, **kwargs)
        if not self.pk:
            self.kind = self.__class__.KIND

    @classmethod
    def add_property(cls, name, func):
        if hasattr(cls, name):
            raise AttributeError("%s has already an attribute %s" %
                                 (cls, name))
        setattr(cls, name, property(func))

    @classproperty
    def KIND(cls):
        return cls._meta.object_name.upper()

    def __unicode__(self):
        return u"%s (%s)" % (_(u"Topology"), self.pk)

    def ispoint(self):
        if not self.pk and self.geom and self.geom.geom_type == 'Point':
            return True
        return all([
            a.start_position == a.end_position
            for a in self.aggregations.all()
        ])

    def add_path(self, path, start=0.0, end=1.0, order=0, reload=True):
        """
        Shortcut function to add paths into this topology.
        """
        from .factories import PathAggregationFactory
        aggr = PathAggregationFactory.create(topo_object=self,
                                             path=path,
                                             start_position=start,
                                             end_position=end,
                                             order=order)

        if self.deleted:
            self.deleted = False
            self.save(update_fields=['deleted'])

        # Since a trigger modifies geom, we reload the object
        if reload:
            self.reload()
        return aggr

    @classmethod
    def overlapping(cls, topologies):
        """ Return a Topology queryset overlapping specified topologies.
        """
        return TopologyHelper.overlapping(cls, topologies)

    def mutate(self, other, delete=True):
        """
        Take alls attributes of the other topology specified and
        save them into this one. Optionnally deletes the other.
        """
        self.offset = other.offset
        self.save(update_fields=['offset'])
        PathAggregation.objects.filter(topo_object=self).delete()
        # The previous operation has put deleted = True (in triggers)
        # and NULL in geom (see update_geometry_of_evenement:: IF t_count = 0)
        self.deleted = False
        self.geom = other.geom
        self.save(update_fields=['deleted', 'geom'])

        # Now copy all agregations from other to self
        aggrs = other.aggregations.all()
        # A point has only one aggregation, except if it is on an intersection.
        # In this case, the trigger will create them, so ignore them here.
        if other.ispoint():
            aggrs = aggrs[:1]
        for aggr in aggrs:
            self.add_path(aggr.path,
                          aggr.start_position,
                          aggr.end_position,
                          aggr.order,
                          reload=False)
        self.reload()
        if delete:
            other.delete(force=True)  # Really delete it from database
        return self

    def reload(self, fromdb=None):
        """
        Reload into instance all computed attributes in triggers.
        """
        if self.pk:
            # Update computed values
            fromdb = self.__class__.objects.get(pk=self.pk)
            self.geom = fromdb.geom
            self.offset = fromdb.offset  # /!\ offset may be set by a trigger OR in
            # the django code, reload() will override
            # any unsaved value
            AltimetryMixin.reload(self, fromdb)
            TimeStampedModel.reload(self, fromdb)
            NoDeleteMixin.reload(self, fromdb)
        return self

    @debug_pg_notices
    def save(self, *args, **kwargs):
        # HACK: these fields are readonly from the Django point of view
        # but they can be changed at DB level. Since Django write all fields
        # to DB anyway, it is important to update it before writting
        if self.pk and settings.TREKKING_TOPOLOGY_ENABLED:
            existing = self.__class__.objects.get(pk=self.pk)
            self.length = existing.length
            # In the case of points, the geom can be set by Django. Don't override.
            point_geom_not_set = self.ispoint() and self.geom is None
            geom_already_in_db = not self.ispoint(
            ) and existing.geom is not None
            if (point_geom_not_set or geom_already_in_db):
                self.geom = existing.geom
        else:
            if not self.deleted and self.geom is None:
                # We cannot have NULL geometry. So we use an empty one,
                # it will be computed or overwritten by triggers.
                self.geom = fromstr('POINT (0 0)')

        if not self.kind:
            if self.KIND == "TOPOLOGYMIXIN":
                raise Exception("Cannot save abstract topologies")
            self.kind = self.__class__.KIND

        # Static value for Topology offset, if any
        shortmodelname = self._meta.object_name.lower().replace('edge', '')
        self.offset = settings.TOPOLOGY_STATIC_OFFSETS.get(
            shortmodelname, self.offset)

        # Save into db
        super(Topology, self).save(*args, **kwargs)
        self.reload()

    def serialize(self, **kwargs):
        return TopologyHelper.serialize(self, **kwargs)

    @classmethod
    def deserialize(cls, serialized):
        return TopologyHelper.deserialize(serialized)