def test_overlapping_returned_can_be_filtered(self): overlaps = Topology.overlapping(self.topo1) overlaps = overlaps.exclude(pk=self.topo1.pk) self.assertEqual(len(overlaps), 4) overlaps = Topology.overlapping(self.topo1) overlaps = overlaps.filter(pk__in=[self.point1.pk, self.point2.pk]) self.assertEqual(len(overlaps), 2)
def test_topology_deserialize(self): p1 = PathFactory.create(geom=LineString((0, 0), (2, 2))) p2 = PathFactory.create(geom=LineString((2, 2), (2, 0))) p3 = PathFactory.create(geom=LineString((2, 0), (4, 0))) pks = [p.pk for p in [p1, p2, p3]] Topology.deserialize( '{"paths": %s, "positions": {"0": [0.3, 1.0], "2": [0.0, 0.7]}, "offset": 1}' % pks) self.assertEqual(Path.objects.count(), 3)
def test_point_at_end_of_path_not_moving_after_mutate(self): PathFactory.create(geom=LineString((400, 400), (410, 400), srid=settings.SRID)) self.assertEqual(1, len(Path.objects.all())) father = Topology.deserialize({"lat": -1, "lng": -1}) poi = Point(500, 600, srid=settings.SRID) poi.transform(settings.API_SRID) son = Topology.deserialize({"lat": poi.y, "lng": poi.x}) father.mutate(son) self.assertTrue(almostequal(father.geom.x, 500)) self.assertTrue(almostequal(father.geom.y, 600))
def forwards(self, orm): # Keep track of previous paths associated to trails by_paths = {} for path, trail in db.execute('SELECT id, sentier FROM l_t_troncon;'): by_paths.setdefault(trail, []).append(path) # Deleting trail reference from path db.delete_column('l_t_troncon', 'sentier') # Fields are now in topology model db.delete_column('l_t_sentier', 'date_update') db.delete_column('l_t_sentier', 'date_insert') # Adding field 'Trail.topo_object' db.add_column('l_t_sentier', 'topo_object', self.gf('django.db.models.fields.related.OneToOneField')( to=orm['core.Topology'], null=True, db_column='evenement'), keep_default=False) # Restore NAIVELY previous trails from geotrek.core.models import Topology trails = db.execute('SELECT id FROM l_t_sentier') print "Migrating %s trails" % len(trails) for (trail, ) in trails: topo = Topology() topo.kind = 'TRAIL' topo.save() print "Created empty topology %s" % topo.pk db.execute('UPDATE l_t_sentier SET evenement = %s WHERE id = %s', (topo.pk, trail)) trail_paths = by_paths.get(trail, []) print "%s paths for trail %s" % (len(trail_paths), trail) for path in trail_paths: db.execute( 'INSERT INTO e_r_evenement_troncon (troncon, evenement, pk_debut, pk_fin, ordre) VALUES (%s,%s, 0.0, 1.0, 1)', (path, topo.pk)) db.execute( 'ALTER TABLE l_t_sentier ALTER COLUMN evenement SET NOT NULL') db.execute( 'ALTER TABLE l_t_sentier DROP CONSTRAINT l_t_sentier_pkey CASCADE') db.execute('ALTER TABLE l_t_sentier DROP COLUMN IF EXISTS id') db.execute( 'ALTER TABLE l_t_sentier ADD CONSTRAINT l_t_sentier_pkey PRIMARY KEY (evenement)' )
def test_point_at_end_of_path_not_moving_after_mutate(self): PathFactory.create( geom=LineString((400, 400), (410, 400), srid=settings.SRID)) self.assertEqual(1, len(Path.objects.all())) father = Topology.deserialize({'lat': -1, 'lng': -1}) poi = Point(500, 600, srid=settings.SRID) poi.transform(settings.API_SRID) son = Topology.deserialize({'lat': poi.y, 'lng': poi.x}) father.mutate(son) self.assertTrue(almostequal(father.geom.x, 500)) self.assertTrue(almostequal(father.geom.y, 600))
def forwards(self, orm): # Keep track of previous paths associated to trails by_paths = {} for path, trail in db.execute("SELECT id, sentier FROM l_t_troncon;"): by_paths.setdefault(trail, []).append(path) # Deleting trail reference from path db.delete_column("l_t_troncon", "sentier") # Fields are now in topology model db.delete_column("l_t_sentier", "date_update") db.delete_column("l_t_sentier", "date_insert") # Adding field 'Trail.topo_object' db.add_column( "l_t_sentier", "topo_object", self.gf("django.db.models.fields.related.OneToOneField")( to=orm["core.Topology"], null=True, db_column="evenement" ), keep_default=False, ) # Restore NAIVELY previous trails from geotrek.core.models import Topology trails = db.execute("SELECT id FROM l_t_sentier") print "Migrating %s trails" % len(trails) for (trail,) in trails: topo = Topology() topo.kind = "TRAIL" topo.save() print "Created empty topology %s" % topo.pk db.execute("UPDATE l_t_sentier SET evenement = %s WHERE id = %s", (topo.pk, trail)) trail_paths = by_paths.get(trail, []) print "%s paths for trail %s" % (len(trail_paths), trail) for path in trail_paths: db.execute( "INSERT INTO e_r_evenement_troncon (troncon, evenement, pk_debut, pk_fin, ordre) VALUES (%s,%s, 0.0, 1.0, 1)", (path, topo.pk), ) db.execute("ALTER TABLE l_t_sentier ALTER COLUMN evenement SET NOT NULL") db.execute("ALTER TABLE l_t_sentier DROP CONSTRAINT l_t_sentier_pkey CASCADE") db.execute("ALTER TABLE l_t_sentier DROP COLUMN IF EXISTS id") db.execute("ALTER TABLE l_t_sentier ADD CONSTRAINT l_t_sentier_pkey PRIMARY KEY (evenement)")
class RestrictedAreaEdge(Topology): topo_object = models.OneToOneField(Topology, parent_link=True, db_column='evenement') restricted_area = models.ForeignKey(RestrictedArea, verbose_name=_(u"Restricted area"), db_column='zone') # Override default manager objects = Topology.get_manager_cls(models.GeoManager)() class Meta: db_table = 'f_t_zonage' verbose_name = _(u"Restricted area edge") verbose_name_plural = _(u"Restricted area edges") def __unicode__(self): return _(u"Restricted area edge") + u": %s" % self.restricted_area @classmethod def path_area_edges(cls, path): return cls.objects.select_related('restricted_area')\ .select_related('restricted_area__area_type')\ .filter(aggregations__path=path).distinct('pk') @classmethod def topology_area_edges(cls, topology): return cls.overlapping(topology).select_related('restricted_area')\ .select_related('restricted_area__area_type')
class CityEdge(Topology): topo_object = models.OneToOneField(Topology, parent_link=True, db_column='evenement') city = models.ForeignKey(City, verbose_name=_(u"City"), db_column='commune') # Override default manager objects = Topology.get_manager_cls(models.GeoManager)() class Meta: db_table = 'f_t_commune' verbose_name = _(u"City edge") verbose_name_plural = _(u"City edges") def __unicode__(self): return _("City edge") + u": %s" % self.city @classmethod def path_city_edges(cls, path): return cls.objects.select_related('city').filter( aggregations__path=path).distinct('pk') @classmethod def topology_city_edges(cls, topology): return cls.overlapping(topology).select_related('city')
def test_return_path_serialized(self): """ Same as test_return_path() but from deserialization. """ p1 = PathFactory.create(geom=LineString((0, 0), (10, 0))) p2 = PathFactory.create(geom=LineString((5, 0), (5, 10), (10, 10))) p3 = Path.objects.filter(name=p1.name).exclude(pk=p1.pk)[0] # Was splitted :) topo = Topology.deserialize( """ [{"offset":0, "positions":{"0":[0.5,1], "1":[0.0, 0.8]}, "paths":[%(p1)s,%(p2)s] }, {"offset":0, "positions":{"0":[0.8,0.0], "1":[0.0, 0.5]}, "paths":[%(p2)s,%(p3)s] } ] """ % {"p1": p1.pk, "p2": p2.pk, "p3": p3.pk} ) topo.save() self.assertEqual(topo.geom, LineString((2.5, 0), (5, 0), (5, 10), (7, 10), (5, 10), (5, 0), (7.5, 0)))
class DistrictEdge(Topology): topo_object = models.OneToOneField(Topology, parent_link=True, db_column='evenement') district = models.ForeignKey(District, verbose_name=_(u"District"), db_column='secteur') # Override default manager objects = Topology.get_manager_cls(models.GeoManager)() class Meta: db_table = 'f_t_secteur' verbose_name = _(u"District edge") verbose_name_plural = _(u"District edges") def __unicode__(self): return _(u"District edge") + u": %s" % self.district @classmethod def path_district_edges(cls, path): return cls.objects.select_related('district').filter( aggregations__path=path).distinct('pk') @classmethod def topology_district_edges(cls, topology): return cls.overlapping(topology).select_related('district')
def filter(qs, edges): """ This piece of code was moved from core, and should be rewritten nicely with managers : TODO ! """ # TODO: this is wrong, land should not depend on maintenance import geotrek.maintenance as maintenance overlapping = Topology.overlapping(edges) # In case, we filter on paths if qs.model == Path: paths = [] for o in overlapping: paths.extend(o.paths.all()) return qs.filter(pk__in=[path.pk for path in set(paths)]) # TODO: This is (amazingly) ugly in terms of OOP. Should refactor overlapping() elif issubclass(qs.model, maintenance.models.Intervention): return qs.filter(topology__in=[topo.pk for topo in overlapping]) elif issubclass(qs.model, maintenance.models.Project): # Find all interventions overlapping those edges interventions = filter(maintenance.models.Intervention.objects.existing() .select_related(depth=1) .filter(project__in=qs), edges) # Return only the projects concerned by the interventions projects = [] for intervention in interventions: projects.append(intervention.project.pk) return qs.filter(pk__in=set(projects)) else: assert issubclass(qs.model, Topology), "%s is not a Topology as expected" % qs.model return qs.filter(pk__in=[topo.pk for topo in overlapping])
def test_point_geom_3d(self): """ + / \ / X \ + + """ p1 = PathFactory.create(geom=LineString((0, 0, 1000), (4, 4, 2000))) p2 = PathFactory.create(geom=LineString((4, 4, 2000), (8, 0, 0))) poi = Point(3, 1, srid=settings.SRID) position, distance = Path.interpolate(p1, poi) self.assertTrue(almostequal(0.5, position)) self.assertTrue(almostequal(-1.414, distance)) # Verify that deserializing this, we obtain the same original coordinates # (use lat/lng as in forms) poi.transform(settings.API_SRID) poitopo = Topology.deserialize({'lat': poi.y, 'lng': poi.x}) # Computed topology properties match original interpolation self.assertTrue(almostequal(0.5, poitopo.aggregations.all()[0].start_position)) self.assertTrue(almostequal(-1.414, poitopo.offset)) # Resulting geometry self.assertTrue(almostequal(3, poitopo.geom.x)) self.assertTrue(almostequal(1, poitopo.geom.y)) self.assertTrue(almostequal(0, poitopo.geom.z))
def test_spoon_loop(self): """ =====<==== || || +-------===<===>===+=====>=== """ p1 = PathFactory.create(geom=LineString((0, 0, 0), (10, 0, 0))) p2 = PathFactory.create(geom=LineString((10, 0, 0), (10, 5, 0), (20, 5, 0), (20, 0, 0), (10, 0, 0))) topo = TopologyFactory.create(no_path=True) topo.add_path(p1, start=0.3, end=1) topo.add_path(p2, start=1, end=0.4) topo.add_path(p2, start=0.4, end=0.4) topo.add_path(p2, start=0.4, end=0.2) topo.add_path(p2, start=0.2, end=0.2) topo.add_path(p2, start=0.2, end=0) topo.add_path(p1, start=1, end=0.3) topo.save() self.assertEqual(topo.geom, LineString((3, 0, 0), (10, 0, 0), (20, 0, 0), (20, 5, 0), (17, 5, 0), (11, 5, 0), # extra point due middle aggregation (10, 5, 0), (10, 0, 0), (3, 0, 0))) # Deserializing should work too topod = Topology.deserialize(""" [{"positions":{"0":[0.3,1],"1":[1, 0.4]},"paths":[%(pk1)s,%(pk2)s]}, {"positions":{"0":[0.4, 0.2]},"paths":[%(pk2)s]}, {"positions":{"0":[0.2,0],"1":[1,0.3]},"paths":[%(pk2)s,%(pk1)s]}]""" % {'pk1': p1.pk, 'pk2': p2.pk}) self.assertEqual(topo.geom, topod.geom) self.assertEqual(len(topod.aggregations.all()), 7)
def test_point_offset_kept(self): """ Shorten path, offset kept. X X +-----------+ +------+ """ p1 = PathFactory.create(geom=LineString((0, 0), (20, 0))) poi = Point(5, 10, srid=settings.SRID) poi.transform(settings.API_SRID) poitopo = Topology.deserialize({'lat': poi.y, 'lng': poi.x}) self.assertTrue( almostequal(0.25, poitopo.aggregations.all()[0].start_position)) self.assertTrue(almostequal(10, poitopo.offset)) p1.geom = LineString((0, 0), (10, 0)) p1.save() poitopo.reload() self.assertTrue(almostequal(10, poitopo.offset)) # Not moved: self.assertTrue(almostequal(5, poitopo.geom.x)) self.assertTrue(almostequal(10, poitopo.geom.y))
def test_point_geom_not_moving(self): """ Modify path, point not moving + + | | \ X / X / \ | | + + """ p1 = PathFactory.create(geom=LineString((0, 0, 0), (0, 5, 0), (5, 10, 0), (0, 15, 0), (0, 20, 0))) poi = Point(10, 10, srid=settings.SRID) poi.transform(settings.API_SRID) poitopo = Topology.deserialize({'lat': poi.y, 'lng': poi.x}) self.assertEqual(0.5, poitopo.aggregations.all()[0].start_position) self.assertTrue(almostequal(-5, poitopo.offset)) # It should have kept its position ! self.assertTrue(almostequal(10, poitopo.geom.x)) self.assertTrue(almostequal(10, poitopo.geom.y)) # Change path, it should still be in the same position p1.geom = LineString((0, 0, 0), (0, 5, 0), (-5, 10, 0), (0, 15, 0), (0, 20, 0)) p1.save() poitopo.reload() self.assertTrue(almostequal(10, poitopo.geom.x)) self.assertTrue(almostequal(10, poitopo.geom.y))
def test_point_offset_updated(self): """ Shorten path, offset updated. X X +-----------+ +------+ """ p1 = PathFactory.create(geom=LineString((0, 0), (20, 0))) poi = Point(10, 10, srid=settings.SRID) poi.transform(settings.API_SRID) poitopo = Topology.deserialize({'lat': poi.y, 'lng': poi.x}) poitopo.save() self.assertAlmostEqual(0.5, poitopo.aggregations.all()[0].start_position, places=6) self.assertAlmostEqual(10, poitopo.offset, places=6) p1.geom = LineString((0, 0), (0, 5)) p1.save() poitopo.reload() self.assertAlmostEqual(11.180339887, poitopo.offset, places=6) # Not moved: self.assertAlmostEqual(10, poitopo.geom.x, places=6) self.assertAlmostEqual(10, poitopo.geom.y, places=6)
def test_point_geom_not_moving(self): r""" Modify path, point not moving + + | | \ X / X / \ | | + + """ p1 = PathFactory.create( geom=LineString((0, 0), (0, 5), (5, 10), (0, 15), (0, 20))) poi = Point(10, 10, srid=settings.SRID) poi.transform(settings.API_SRID) poitopo = Topology.deserialize({'lat': poi.y, 'lng': poi.x}) self.assertEqual(0.5, poitopo.aggregations.all()[0].start_position) self.assertAlmostEqual(-5, poitopo.offset, places=6) # It should have kept its position ! self.assertAlmostEqual(10, poitopo.geom.x, places=6) self.assertAlmostEqual(10, poitopo.geom.y, places=6) # Change path, it should still be in the same position p1.geom = LineString((0, 0), (0, 5), (-5, 10), (0, 15), (0, 20)) p1.save() poitopo.reload() self.assertAlmostEqual(10, poitopo.geom.x, places=6) self.assertAlmostEqual(10, poitopo.geom.y, places=6)
def test_point_geom_3d(self): """ + / \ / X \ + + """ p1 = PathFactory.create(geom=LineString((0, 0), (4, 4))) PathFactory.create(geom=LineString((4, 4), (8, 0))) poi = Point(3, 1, srid=settings.SRID) position, distance = Path.interpolate(p1, poi) self.assertAlmostEqual(0.5, position, places=6) self.assertAlmostEqual(-1.414, distance, places=2) # Verify that deserializing this, we obtain the same original coordinates # (use lat/lng as in forms) poi.transform(settings.API_SRID) poitopo = Topology.deserialize({'lat': poi.y, 'lng': poi.x}) # Computed topology properties match original interpolation self.assertAlmostEqual(0.5, poitopo.aggregations.all()[0].start_position, places=6) self.assertAlmostEqual(-1.414, poitopo.offset, places=2) # Resulting geometry self.assertAlmostEqual(3, poitopo.geom.x, places=6) self.assertAlmostEqual(1, poitopo.geom.y, places=6)
def test_spoon_loop_2(self): """ =====>==== || || +-------===<===>===+=====<=== """ p1 = PathFactory.create(geom=LineString((0, 0, 0), (10, 0, 0))) p2 = PathFactory.create(geom=LineString((10, 0, 0), (10, 5, 0), (20, 5, 0), (20, 0, 0), (10, 0, 0))) topo = TopologyFactory.create(no_path=True) topo.add_path(p1, start=0.3, end=1) topo.add_path(p2, start=0, end=0.4) topo.add_path(p2, start=0.4, end=0.4) topo.add_path(p2, start=0.4, end=0.8) topo.add_path(p2, start=0.8, end=0.8) topo.add_path(p2, start=0.8, end=1.0) topo.add_path(p1, start=1, end=0.3) topo.save() self.assertEqual(topo.geom, LineString((3, 0, 0), (10, 0, 0), (10, 5, 0), (17, 5, 0), (20, 5, 0), # extra point due middle aggregation (20, 0, 0), (16, 0, 0), (10, 0, 0), (3, 0, 0))) # De/Serializing should work too serialized = """ [{"kind": "TOPOLOGY","positions":{"0":[0.3,1],"1":[0, 0.4]},"paths":[%(pk1)s,%(pk2)s],"offset": 0.0}, {"kind": "TOPOLOGY","positions":{"0":[0.4, 0.8]},"paths":[%(pk2)s],"offset": 0.0}, {"kind": "TOPOLOGY","positions":{"0":[0.8,1],"1":[1,0.3]},"paths":[%(pk2)s,%(pk1)s],"offset": 0.0}]""" % {'pk1': p1.pk, 'pk2': p2.pk} self.assertEqual(json.loads(serialized), json.loads(topo.serialize())) topod = Topology.deserialize(serialized) self.assertEqual(topo.geom, topod.geom) self.assertEqual(len(topod.aggregations.all()), 7)
def test_return_path_serialized(self): """ Same as test_return_path() but from deserialization. """ p1 = PathFactory.create(geom=LineString((0, 0), (10, 0))) p2 = PathFactory.create(geom=LineString((5, 0), (5, 10), (10, 10))) p3 = Path.objects.filter(name=p1.name).exclude( pk=p1.pk)[0] # Was splitted :) topo = Topology.deserialize(""" [{"offset":0, "positions":{"0":[0.5,1], "1":[0.0, 0.8]}, "paths":[%(p1)s,%(p2)s] }, {"offset":0, "positions":{"0":[0.8,0.0], "1":[0.0, 0.5]}, "paths":[%(p2)s,%(p3)s] } ] """ % { 'p1': p1.pk, 'p2': p2.pk, 'p3': p3.pk }) topo.kind = 'TOPOLOGY' topo.save() self.assertEqual( topo.geom, LineString((2.5, 0), (5, 0), (5, 10), (7, 10), (5, 10), (5, 0), (7.5, 0), srid=settings.SRID))
def test_topology_deserialize_point(self): PathFactory.create( geom=LineString((699999, 6600001), (700001, 6600001))) topology = Topology.deserialize('{"lat": 46.5, "lng": 3}') self.assertEqual(topology.offset, -1) self.assertEqual(topology.aggregations.count(), 1) self.assertEqual(topology.aggregations.get().start_position, .5) self.assertEqual(topology.aggregations.get().end_position, .5)
def create_infrastructure(self, geometry, name, type, category, use_structure, condition, structure, description, year, verbosity, eid): infra_type, created = InfrastructureType.objects.get_or_create( label=type, type=category, structure=structure if use_structure else None) if created and verbosity: self.stdout.write( "- InfrastructureType '{}' created".format(infra_type)) if condition: condition_type, created = InfrastructureCondition.objects.get_or_create( label=condition, structure=structure if use_structure else None) if created and verbosity: self.stdout.write( "- Condition Type '{}' created".format(condition_type)) else: condition_type = None with transaction.atomic(): fields_without_eid = { 'type': infra_type, 'name': name, 'condition': condition_type, 'structure': structure, 'description': description, 'implantation_year': year } if eid: infra, created = Infrastructure.objects.update_or_create( eid=eid, defaults=fields_without_eid) if verbosity > 0 and not created: self.stdout.write("Update : %s with eid %s" % (name, eid)) else: infra = Infrastructure.objects.create(**fields_without_eid) if settings.TREKKING_TOPOLOGY_ENABLED: try: geometry.coord_dim = 2 geometry = geometry.transform(settings.API_SRID, clone=True) serialized = '{"lng": %s, "lat": %s}' % (geometry.x, geometry.y) topology = Topology.deserialize(serialized) infra.mutate(topology) except IndexError: raise GEOSException('Invalid Geometry type. You need 1 path') else: if geometry.geom_type != 'Point': raise GEOSException('Invalid Geometry type.') geometry = geometry.transform(settings.SRID, clone=True) infra.geom = Point(geometry.x, geometry.y) infra.save() self.counter += 1 return infra
def test_topology_deserialize_point_with_snap(self): path = PathFactory.create( geom=LineString((699999, 6600001), (700001, 6600001))) topology = Topology.deserialize('{"lat": 46.5, "lng": 3, "snap": %s}' % path.pk) self.assertEqual(topology.offset, 0) self.assertEqual(topology.aggregations.count(), 1) self.assertEqual(topology.aggregations.get().start_position, .5) self.assertEqual(topology.aggregations.get().end_position, .5)
def test_deserialize_multiple_lines(self): # Multiple paths p1 = PathFactory.create(geom=LineString((0, 0, 0), (2, 2, 2))) p2 = PathFactory.create(geom=LineString((2, 2, 2), (2, 0, 0))) p3 = PathFactory.create(geom=LineString((2, 0, 0), (4, 0, 0))) pks = [p.pk for p in [p1, p2, p3]] topology = Topology.deserialize('{"paths": %s, "positions": {"0": [0.0, 1.0], "2": [0.0, 1.0]}, "offset": 1}' % (pks)) for i in range(3): self.assertEqual(topology.aggregations.all()[i].start_position, 0.0) self.assertEqual(topology.aggregations.all()[i].end_position, 1.0) topology = Topology.deserialize('{"paths": %s, "positions": {"0": [0.3, 1.0], "2": [0.0, 0.7]}, "offset": 1}' % (pks)) self.assertEqual(topology.aggregations.all()[0].start_position, 0.3) self.assertEqual(topology.aggregations.all()[0].end_position, 1.0) self.assertEqual(topology.aggregations.all()[1].start_position, 0.0) self.assertEqual(topology.aggregations.all()[1].end_position, 1.0) self.assertEqual(topology.aggregations.all()[2].start_position, 0.0) self.assertEqual(topology.aggregations.all()[2].end_position, 0.7)
def test_deserialize_line(self): path = PathFactory.create() topology = Topology.deserialize('[{"paths": [%s], "positions": {"0": [0.0, 1.0]}, "offset": 1}]' % (path.pk)) self.assertEqual(topology.offset, 1) self.assertEqual(topology.kind, Topology.KIND) self.assertEqual(len(topology.paths.all()), 1) self.assertEqual(topology.aggregations.all()[0].path, path) self.assertEqual(topology.aggregations.all()[0].start_position, 0.0) self.assertEqual(topology.aggregations.all()[0].end_position, 1.0)
class POI(PicturesMixin, MapEntityMixin, Topology): topo_object = models.OneToOneField(Topology, parent_link=True, db_column='evenement') name = models.CharField(verbose_name=_(u"Name"), max_length=128, db_column='nom', help_text=_(u"Official name")) description = models.TextField(verbose_name=_(u"Description"), db_column='description', help_text=_(u"History, details, ...")) type = models.ForeignKey('POIType', related_name='pois', verbose_name=_(u"Type"), db_column='type') class Meta: db_table = 'o_t_poi' verbose_name = _(u"POI") verbose_name_plural = _(u"POI") # Override default manager objects = Topology.get_manager_cls(POIManager)() def __unicode__(self): return u"%s (%s)" % (self.name, self.type) @property def type_display(self): return unicode(self.type) @property def name_display(self): return u'<a data-pk="%s" href="%s" >%s</a>' % ( self.pk, self.get_detail_url(), self.name) @property def name_csv_display(self): return unicode(self.name) @property def serializable_type(self): return { 'label': self.type.label, 'pictogram': self.type.serializable_pictogram } @classmethod def path_pois(cls, path): return cls.objects.filter(aggregations__path=path).distinct('pk') @classmethod def topology_pois(cls, topology): return cls.overlapping(topology)
class LandEdge(MapEntityMixin, Topology): topo_object = models.OneToOneField(Topology, parent_link=True, db_column='evenement') land_type = models.ForeignKey(LandType, verbose_name=_(u"Land type"), db_column='type') owner = models.TextField(verbose_name=_(u"Owner"), db_column='proprietaire', blank=True) agreement = models.BooleanField(verbose_name=_(u"Agreement"), db_column='convention', default=False) eid = models.CharField(verbose_name=_(u"External id"), max_length=1024, blank=True, null=True, db_column='id_externe') # Override default manager objects = Topology.get_manager_cls(models.GeoManager)() class Meta: db_table = 'f_t_foncier' verbose_name = _(u"Land edge") verbose_name_plural = _(u"Land edges") def __unicode__(self): return _(u"Land edge") + u": %s" % self.land_type @property def color_index(self): return self.land_type_id @property def name(self): return self.land_type_csv_display @property def land_type_display(self): return u'<a data-pk="%s" href="%s" >%s</a>' % ( self.pk, self.get_detail_url(), self.land_type) @property def land_type_csv_display(self): return unicode(self.land_type) @classmethod def path_lands(cls, path): return cls.objects.existing().select_related('land_type').filter( aggregations__path=path).distinct('pk') @classmethod def topology_lands(cls, topology): return cls.overlapping(topology).select_related('land_type')
def get_interventions(cls, obj): blade_content_type = ContentType.objects.get_for_model(Blade) if settings.TREKKING_TOPOLOGY_ENABLED: topologies = list(Topology.overlapping(obj).values_list('pk', flat=True)) else: area = obj.geom.buffer(settings.INTERVENTION_INTERSECTION_MARGIN) topologies = list(Topology.objects.existing().filter(geom__intersects=area).values_list('pk', flat=True)) qs = Q(target_id__in=topologies) & ~Q(target_type=blade_content_type) if 'geotrek.signage' in settings.INSTALLED_APPS: blades = list(Blade.objects.filter(signage__in=topologies).values_list('id', flat=True)) qs |= Q(target_id__in=blades, target_type=blade_content_type) return Intervention.objects.existing().filter(qs).distinct('pk')
def test_deserialize_serialize(self): path = PathFactory.create(geom=LineString((1, 1), (2, 2), (2, 0))) before = TopologyFactory.create(offset=1, paths=[(path, 0.5, 0.5)]) # Deserialize its serialized version ! after = Topology.deserialize(before.serialize()) self.assertEqual(len(before.paths.all()), len(after.paths.all())) start_before = before.aggregations.all()[0].start_position end_before = before.aggregations.all()[0].end_position start_after = after.aggregations.all()[0].start_position end_after = after.aggregations.all()[0].end_position self.assertAlmostEqual(start_before, start_after, places=6) self.assertAlmostEqual(end_before, end_after, places=6)
class CompetenceEdge(MapEntityMixin, Topology): topo_object = models.OneToOneField(Topology, parent_link=True, db_column='evenement') organization = models.ForeignKey(Organism, verbose_name=_(u"Organism"), db_column='organisme') eid = models.CharField(verbose_name=_(u"External id"), max_length=1024, blank=True, null=True, db_column='id_externe') # Override default manager objects = Topology.get_manager_cls(models.GeoManager)() class Meta: db_table = 'f_t_competence' verbose_name = _(u"Competence edge") verbose_name_plural = _(u"Competence edges") def __unicode__(self): return _(u"Competence edge") + u": %s" % self.organization @property def color_index(self): return self.organization_id @property def name(self): return self.organization_csv_display @property def organization_display(self): return u'<a data-pk="%s" href="%s" >%s</a>' % ( self.pk, self.get_detail_url(), self.organization) @property def organization_csv_display(self): return unicode(self.organization) @classmethod def path_competences(cls, path): return cls.objects.existing().select_related('organization').filter( aggregations__path=path).distinct('pk') @classmethod def topology_competences(cls, topology): return cls.overlapping(Topology.objects.get( pk=topology.pk)).select_related('organization')
def test_point_geom_moving(self): p1 = PathFactory.create(geom=LineString((0, 0), (0, 5))) poi = Point(0, 2.5, srid=settings.SRID) poi.transform(settings.API_SRID) poitopo = Topology.deserialize({"lat": poi.y, "lng": poi.x}) self.assertTrue(almostequal(0.5, poitopo.aggregations.all()[0].start_position)) self.assertTrue(almostequal(0, poitopo.offset)) self.assertTrue(almostequal(0, poitopo.geom.x)) self.assertTrue(almostequal(2.5, poitopo.geom.y)) p1.geom = LineString((10, 0), (10, 5)) p1.save() poitopo.reload() self.assertTrue(almostequal(10, poitopo.geom.x)) self.assertTrue(almostequal(2.5, poitopo.geom.y))
def test_deserialize_serialize(self): path = PathFactory.create(geom=LineString((1, 1), (2, 2), (2, 0))) before = TopologyFactory.create(offset=1, no_path=True) before.add_path(path, start=0.5, end=0.5) # Deserialize its serialized version ! after = Topology.deserialize(before.serialize()) self.assertEqual(len(before.paths.all()), len(after.paths.all())) start_before = before.aggregations.all()[0].start_position end_before = before.aggregations.all()[0].end_position start_after = after.aggregations.all()[0].start_position end_after = after.aggregations.all()[0].end_position self.assertTrue(almostequal(start_before, start_after), '%s != %s' % (start_before, start_after)) self.assertTrue(almostequal(end_before, end_after), '%s != %s' % (end_before, end_after))
def test_deserialize_point(self): PathFactory.create() # Take a point p = Point(2, 1, 0, srid=settings.SRID) p.transform(settings.API_SRID) closest = Path.closest(p) # Check closest path self.assertEqual(closest.geom.coords, ((1.0, 1.0, 0.0), (2.0, 2.0, 0.0))) # The point has same x as first point of path, and y to 0 : topology = Topology.deserialize('{"lng": %s, "lat": %s}' % (p.x, p.y)) self.assertAlmostEqual(topology.offset, -0.7071, 3) self.assertEqual(len(topology.paths.all()), 1) pagg = topology.aggregations.get() self.assertTrue(almostequal(pagg.start_position, 0.5)) self.assertTrue(almostequal(pagg.end_position, 0.5))
class SignageManagementEdge(MapEntityMixin, Topology): topo_object = models.OneToOneField(Topology, parent_link=True, db_column='evenement') organization = models.ForeignKey(Organism, verbose_name=_(u"Organism"), db_column='organisme') # Override default manager objects = Topology.get_manager_cls(models.GeoManager)() class Meta: db_table = 'f_t_gestion_signaletique' verbose_name = _(u"Signage management edge") verbose_name_plural = _(u"Signage management edges") def __unicode__(self): return _(u"Signage management edge") + u": %s" % self.organization @property def color_index(self): return self.organization_id @property def name(self): return self.organization_csv_display @property def organization_display(self): return self.display @property def organization_csv_display(self): return unicode(self.organization) @property def display(self): return u'<a data-pk="%s" href="%s" >%s</a>' % ( self.pk, self.get_detail_url(), self.organization) @classmethod def path_signages(cls, path): return cls.objects.select_related('organization').filter( aggregations__path=path).distinct('pk') @classmethod def topology_signages(cls, topology): return cls.overlapping(topology).select_related('organization')
class PhysicalEdge(MapEntityMixin, Topology): topo_object = models.OneToOneField(Topology, parent_link=True, db_column='evenement') physical_type = models.ForeignKey(PhysicalType, verbose_name=_(u"Physical type"), db_column='type') # Override default manager objects = Topology.get_manager_cls(models.GeoManager)() class Meta: db_table = 'f_t_nature' verbose_name = _(u"Physical edge") verbose_name_plural = _(u"Physical edges") def __unicode__(self): return _(u"Physical edge") + u": %s" % self.physical_type @property def color_index(self): return self.physical_type_id @property def name(self): return self.physical_type_csv_display @property def physical_type_display(self): return self.display @property def physical_type_csv_display(self): return unicode(self.physical_type) @property def display(self): return u'<a data-pk="%s" href="%s" >%s</a>' % ( self.pk, self.get_detail_url(), self.physical_type) @classmethod def path_physicals(cls, path): return cls.objects.select_related('physical_type').filter( aggregations__path=path).distinct('pk') @classmethod def topology_physicals(cls, topology): return cls.overlapping(topology).select_related('physical_type')
def test_point_geom_moving(self): p1 = PathFactory.create(geom=LineString((0, 0), (0, 5))) poi = Point(0, 2.5, srid=settings.SRID) poi.transform(settings.API_SRID) poitopo = Topology.deserialize({'lat': poi.y, 'lng': poi.x}) self.assertTrue( almostequal(0.5, poitopo.aggregations.all()[0].start_position)) self.assertTrue(almostequal(0, poitopo.offset)) self.assertTrue(almostequal(0, poitopo.geom.x)) self.assertTrue(almostequal(2.5, poitopo.geom.y)) p1.geom = LineString((10, 0), (10, 5)) p1.save() poitopo.reload() self.assertTrue(almostequal(10, poitopo.geom.x)) self.assertTrue(almostequal(2.5, poitopo.geom.y))
def test_deserialize_serialize(self): path = PathFactory.create(geom=LineString((1, 1, 1), (2, 2, 2), (2, 0, 0))) before = TopologyFactory.create(offset=1, no_path=True) before.add_path(path, start=0.5, end=0.5) # Reload from DB before = Topology.objects.get(pk=before.pk) # Deserialize its serialized version ! after = Topology.deserialize(before.serialize()) # Reload from DB after = Topology.objects.get(pk=after.pk) self.assertEqual(len(before.paths.all()), len(after.paths.all())) self.assertTrue(almostequal(before.aggregations.all()[0].start_position, after.aggregations.all()[0].start_position)) self.assertTrue(almostequal(before.aggregations.all()[0].end_position, after.aggregations.all()[0].end_position))
def test_point_offset_updated(self): """ Shorten path, offset updated. X X +-----------+ +------+ """ p1 = PathFactory.create(geom=LineString((0, 0), (20, 0))) poi = Point(10, 10, srid=settings.SRID) poi.transform(settings.API_SRID) poitopo = Topology.deserialize({'lat': poi.y, 'lng': poi.x}) self.assertTrue(almostequal(0.5, poitopo.aggregations.all()[0].start_position)) self.assertTrue(almostequal(10, poitopo.offset)) p1.geom = LineString((0, 0), (0, 5)) p1.save() poitopo.reload() self.assertTrue(almostequal(11.180339887, poitopo.offset)) # Not moved: self.assertTrue(almostequal(10, poitopo.geom.x)) self.assertTrue(almostequal(10, poitopo.geom.y))
def test_point_offset_kept(self): """ Shorten path, offset kept. X X +-----------+ +------+ """ p1 = PathFactory.create(geom=LineString((0, 0), (20, 0))) poi = Point(5, 10, srid=settings.SRID) poi.transform(settings.API_SRID) poitopo = Topology.deserialize({"lat": poi.y, "lng": poi.x}) self.assertTrue(almostequal(0.25, poitopo.aggregations.all()[0].start_position)) self.assertTrue(almostequal(10, poitopo.offset)) p1.geom = LineString((0, 0), (10, 0)) p1.save() poitopo.reload() self.assertTrue(almostequal(10, poitopo.offset)) # Not moved: self.assertTrue(almostequal(5, poitopo.geom.x)) self.assertTrue(almostequal(10, poitopo.geom.y))
) @property def physical_type_csv_display(self): return unicode(self.physical_type) @classmethod def path_physicals(cls, path): return cls.objects.select_related('physical_type').filter(aggregations__path=path).distinct('pk') @classmethod def topology_physicals(cls, topology): return cls.overlapping(topology).select_related('physical_type') Path.add_property('physical_edges', PhysicalEdge.path_physicals) Topology.add_property('physical_edges', PhysicalEdge.topology_physicals) Intervention.add_property('physical_edges', lambda self: self.topology.physical_edges if self.topology else []) Project.add_property('physical_edges', lambda self: self.edges_by_attr('physical_edges')) class LandType(StructureRelated): name = models.CharField(max_length=128, db_column='foncier', verbose_name=_(u"Name")) right_of_way = models.BooleanField(db_column='droit_de_passage', verbose_name=_(u"Right of way")) class Meta: db_table = 'f_b_foncier' verbose_name = _(u"Land type") verbose_name_plural = _(u"Land types") ordering = ['name'] def __unicode__(self):
def test_overlapping_return_sharing_path(self): overlaps = Topology.overlapping(self.topo1) self.assertTrue(self.topo1 in overlaps) self.assertTrue(self.topo2 in overlaps)
def test_overlapping_sorts_by_order_of_progression(self): overlaps = Topology.overlapping(self.topo2) self.assertEqual(list(overlaps), [self.topo2, self.point1, self.point3, self.point2, self.topo1])
def test_overlapping_sorts_when_path_is_reversed(self): overlaps = Topology.overlapping(self.topo1) self.assertEqual(list(overlaps), [self.topo1, self.point2, self.point3, self.point1, self.topo2])
] if settings.SHOW_SENSITIVE_AREAS_ON_MAP_SCREENSHOT: maplayers.append(ugettext(u"Sensitive area")) if settings.SHOW_POIS_ON_MAP_SCREENSHOT: maplayers.append(ugettext(u"POIs")) if settings.SHOW_SERVICES_ON_MAP_SCREENSHOT: maplayers.append(ugettext(u"Services")) if settings.SHOW_SIGNAGES_ON_MAP_SCREENSHOT: maplayers.append(ugettext(u"Signages")) if settings.SHOW_INFRASTRUCTURES_ON_MAP_SCREENSHOT: maplayers.append(ugettext(u"Infrastructures")) return {"maplayers": maplayers} Path.add_property('treks', Trek.path_treks, _(u"Treks")) Topology.add_property('treks', Trek.topology_treks, _(u"Treks")) if settings.HIDE_PUBLISHED_TREKS_IN_TOPOLOGIES: Topology.add_property('published_treks', lambda self: [], _(u"Published treks")) else: Topology.add_property('published_treks', lambda self: intersecting(Trek, self).filter(published=True), _(u"Published treks")) Intervention.add_property('treks', lambda self: self.topology.treks if self.topology else [], _(u"Treks")) Project.add_property('treks', lambda self: self.edges_by_attr('treks'), _(u"Treks")) tourism_models.TouristicContent.add_property('treks', lambda self: intersecting(Trek, self), _(u"Treks")) tourism_models.TouristicContent.add_property('published_treks', lambda self: intersecting(Trek, self).filter(published=True), _(u"Published treks")) tourism_models.TouristicEvent.add_property('treks', lambda self: intersecting(Trek, self), _(u"Treks")) tourism_models.TouristicEvent.add_property('published_treks', lambda self: intersecting(Trek, self).filter(published=True), _(u"Published treks")) class TrekRelationshipManager(models.Manager): use_for_related_fields = True
proxy = True verbose_name = _(u"Infrastructure") verbose_name_plural = _(u"Infrastructures") @classmethod def path_infrastructures(cls, path): return cls.objects.existing().filter(aggregations__path=path).distinct("pk") @classmethod def topology_infrastructures(cls, topology): return cls.overlapping(topology) Path.add_property("infrastructures", lambda self: Infrastructure.path_infrastructures(self), _(u"Infrastructures")) Topology.add_property( "infrastructures", lambda self: Infrastructure.topology_infrastructures(self), _(u"Infrastructures") ) class SignageGISManager(gismodels.GeoManager): """ Overide default typology mixin manager, and filter by type. """ def get_queryset(self): return super(SignageGISManager, self).get_queryset().filter(type__type=INFRASTRUCTURE_TYPES.SIGNAGE) class SignageStructureManager(StructureRelatedManager): """ Overide default structure related manager, and filter by type. """ def get_queryset(self): return super(SignageStructureManager, self).get_queryset().filter(type__type=INFRASTRUCTURE_TYPES.SIGNAGE)
return unicode(self.physical_type) @property def display(self): return u'<a data-pk="%s" href="%s" >%s</a>' % (self.pk, self.get_detail_url(), self.physical_type) @classmethod def path_physicals(cls, path): return cls.objects.select_related('physical_type').filter(aggregations__path=path).distinct('pk') @classmethod def topology_physicals(cls, topology): return cls.objects.select_related('physical_type').filter(aggregations__path__in=topology.paths.all()).distinct('pk') Path.add_property('physical_edges', PhysicalEdge.path_physicals) Topology.add_property('physical_edges', PhysicalEdge.topology_physicals) Intervention.add_property('physical_edges', lambda self: self.topology.physical_edges if self.topology else []) Project.add_property('physical_edges', lambda self: self.edges_by_attr('physical_edges')) class LandType(StructureRelated): name = models.CharField(max_length=128, db_column='foncier', verbose_name=_(u"Name")) right_of_way = models.BooleanField(db_column='droit_de_passage', verbose_name=_(u"Right of way")) class Meta: db_table = 'f_b_foncier' verbose_name = _(u"Land type") verbose_name_plural = _(u"Land types") ordering = ['name'] def __unicode__(self):
def test_overlapping_does_not_fail_if_no_records(self): from geotrek.trekking.models import Trek overlaps = Topology.overlapping(Trek.objects.all()) self.assertEqual(list(overlaps), [])
treks = cls.objects.existing().filter(aggregations__path=path) # The following part prevents conflict with default trek ordering # ProgrammingError: SELECT DISTINCT ON expressions must match initial ORDER BY expressions return treks.order_by('topo_object').distinct('topo_object') @classmethod def topology_treks(cls, topology): if settings.TREKKING_TOPOLOGY_ENABLED: qs = cls.overlapping(topology) else: area = topology.geom.buffer(settings.TREK_POI_INTERSECTION_MARGIN) qs = cls.objects.filter(geom__intersects=area) return qs Path.add_property('treks', Trek.path_treks) Topology.add_property('treks', Trek.topology_treks) Intervention.add_property('treks', lambda self: self.topology.treks if self.topology else []) Project.add_property('treks', lambda self: self.edges_by_attr('treks')) class TrekRelationshipManager(models.Manager): use_for_related_fields = True def get_queryset(self): # Select treks foreign keys by default qs = super(TrekRelationshipManager, self).get_queryset().select_related('trek_a', 'trek_b') # Exclude deleted treks return qs.exclude(trek_a__deleted=True).exclude(trek_b__deleted=True) class TrekRelationship(models.Model):
) @property def physical_type_csv_display(self): return unicode(self.physical_type) @classmethod def path_physicals(cls, path): return cls.objects.existing().select_related('physical_type').filter(aggregations__path=path).distinct('pk') @classmethod def topology_physicals(cls, topology): return cls.overlapping(topology).select_related('physical_type') Path.add_property('physical_edges', PhysicalEdge.path_physicals, _(u"Physical edges")) Topology.add_property('physical_edges', PhysicalEdge.topology_physicals, _(u"Physical edges")) Intervention.add_property('physical_edges', lambda self: self.topology.physical_edges if self.topology else [], _(u"Physical edges")) Project.add_property('physical_edges', lambda self: self.edges_by_attr('physical_edges'), _(u"Physical edges")) class LandType(StructureRelated): name = models.CharField(max_length=128, db_column='foncier', verbose_name=_(u"Name")) right_of_way = models.BooleanField(db_column='droit_de_passage', verbose_name=_(u"Right of way")) class Meta: db_table = 'f_b_foncier' verbose_name = _(u"Land type") verbose_name_plural = _(u"Land types") ordering = ['name'] def __unicode__(self):
def type2_label(self): return self.category.type2_label @property def types1_display(self): return ', '.join([unicode(n) for n in self.type1.all()]) @property def types2_display(self): return ', '.join([unicode(n) for n in self.type2.all()]) @property def prefixed_category_id(self): return self.category.prefixed_id Topology.add_property('touristic_contents', lambda self: intersecting(TouristicContent, self, distance=settings.TOURISM_INTERSECTION_MARGIN), _(u"Touristic contents")) Topology.add_property('published_touristic_contents', lambda self: intersecting(TouristicContent, self, distance=settings.TOURISM_INTERSECTION_MARGIN).filter(published=True), _(u"Published touristic contents")) TouristicContent.add_property('touristic_contents', lambda self: intersecting(TouristicContent, self, distance=settings.TOURISM_INTERSECTION_MARGIN), _(u"Touristic contents")) TouristicContent.add_property('published_touristic_contents', lambda self: intersecting(TouristicContent, self, distance=settings.TOURISM_INTERSECTION_MARGIN).filter(published=True), _(u"Published touristic contents")) class TouristicEventType(OptionalPictogramMixin): type = models.CharField(verbose_name=_(u"Type"), max_length=128, db_column='type') class Meta: db_table = 't_b_evenement_touristique_type' verbose_name = _(u"Touristic event type") verbose_name_plural = _(u"Touristic event types") ordering = ['type']
@property def geomtransform(self): geom = self.topo_object.geom return geom.transform(settings.API_SRID, clone=True) @property def lat_value(self): return self.geomtransform.x @property def lng_value(self): return self.geomtransform.y Path.add_property('signages', lambda self: Signage.path_signages(self), _(u"Signages")) Topology.add_property('signages', lambda self: Signage.topology_signages(self), _(u"Signages")) Topology.add_property('published_signages', lambda self: Signage.published_topology_signages(self), _(u"Published Signages")) class Direction(models.Model): label = models.CharField(db_column="etiquette", max_length=128) class Meta: db_table = 's_b_direction' verbose_name = _(u"Direction") verbose_name_plural = _(u"Directions") def __unicode__(self): return self.label
return cls.objects.existing()\ .select_related('restricted_area')\ .select_related('restricted_area__area_type')\ .filter(aggregations__path=path).distinct('pk') @classmethod def topology_area_edges(cls, topology): return cls.overlapping(topology)\ .select_related('restricted_area')\ .select_related('restricted_area__area_type') if settings.TREKKING_TOPOLOGY_ENABLED: Path.add_property('area_edges', RestrictedAreaEdge.path_area_edges) Path.add_property('areas', lambda self: uniquify(map(attrgetter('restricted_area'), self.area_edges))) Topology.add_property('area_edges', RestrictedAreaEdge.topology_area_edges) Topology.add_property('areas', lambda self: uniquify(map(attrgetter('restricted_area'), self.area_edges))) Intervention.add_property('area_edges', lambda self: self.topology.area_edges if self.topology else []) Intervention.add_property('areas', lambda self: self.topology.areas if self.topology else []) Project.add_property('area_edges', lambda self: self.edges_by_attr('area_edges')) Project.add_property('areas', lambda self: uniquify(map(attrgetter('restricted_area'), self.area_edges))) else: Topology.add_property('areas', lambda self: intersecting(RestrictedArea, self)) TouristicContent.add_property('areas', lambda self: intersecting(RestrictedArea, self)) TouristicEvent.add_property('areas', lambda self: intersecting(RestrictedArea, self)) class City(models.Model): code = models.CharField(primary_key=True, max_length=6, db_column='insee') name = models.CharField(max_length=128, db_column='commune', verbose_name=_(u"Name"))