Пример #1
0
def intersections_byways(b1, b2):
   intersections = list()
   points1 = gml.flat_to_xys(b1.geometry_wkt)
   points2 = gml.flat_to_xys(b2.geometry_wkt)
   for i in range(1,len(points1)):
      for j in range(1, len(points2)):
         intersection = intersection_segments((points1[i-1], points1[i]),
                                              (points2[j-1], points2[j]))
         if (intersection is not None):
            intersections.append(intersection)
   return intersections
Пример #2
0
    def fit_route_step(self, step, at_start):

        # Get the node ID and node x,y from the byway for this route step. That
        # is, instead of using the geocoded xy, use Cyclopath's byway's xy.
        # FIXME: If you request a route from an address in the middle of the
        #        street, doesn't think move the point to the nearest
        #        intersection?? (TEST: 5038 dupont ave s)

        # FIXED?: Was: geom = gml.flat_to_xys(step.geometry[2:])
        geom = gml.flat_to_xys(step.geometry)

        if at_start:
            if step.forward:
                self.node_id = step.beg_node_id
                self.x = geom[0][0]
                self.y = geom[0][1]
            else:
                self.node_id = step.fin_node_id
                self.x = geom[-1][0]
                self.y = geom[-1][1]
        else:
            if step.forward:
                self.node_id = step.fin_node_id
                self.x = geom[-1][0]
                self.y = geom[-1][1]
            else:
                self.node_id = step.beg_node_id
                self.x = geom[0][0]
                self.y = geom[0][1]
Пример #3
0
 def fix_onepoint(block):
    points = gml.flat_to_xys(block.geometry_wkt)
    if len(points) == 3:
       # remove extra point, we don't need it anymore
       del points[1]
       block.geometry_wkt = (
          'LINESTRING(%s)' % (gml.wkt_coords_format(points),))
Пример #4
0
   def fit_route_step(self, step, at_start):

      # Get the node ID and node x,y from the byway for this route step. That
      # is, instead of using the geocoded xy, use Cyclopath's byway's xy.
      # FIXME: If you request a route from an address in the middle of the
      #        street, doesn't think move the point to the nearest
      #        intersection?? (TEST: 5038 dupont ave s)

      # FIXED?: Was: geom = gml.flat_to_xys(step.geometry[2:])
      geom = gml.flat_to_xys(step.geometry)

      if at_start:
         if step.forward:
            self.node_id = step.beg_node_id
            self.x = geom[0][0]
            self.y = geom[0][1]
         else:
            self.node_id = step.fin_node_id
            self.x = geom[-1][0]
            self.y = geom[-1][1]
      else:
         if step.forward:
            self.node_id = step.fin_node_id
            self.x = geom[-1][0]
            self.y = geom[-1][1]
         else:
            self.node_id = step.beg_node_id
            self.x = geom[0][0]
            self.y = geom[0][1]
Пример #5
0
 def save_core(self, qb):
     item_user_watching.One.save_core(self, qb)
     # A branch's stack ID always the same as its branch ID (self-referential)
     g.assurt(
         self.stack_id == self.branch_id)  # see save_core_get_branch_id
     # NOTE: We only honor create-branch and delete from local requests. From
     #       flashclient, we just support renaming.
     g.assurt(((self.version > 1) and not self.deleted)
              or qb.request_is_local)
     # NOTE: We've already verified the parent ID is valid. If the item is
     # fresh, we've checked that the user has permissions to make a copy of
     # the parent. If not, we've checked the user can edit the branch.
     if self.fresh:
         self.parent_id = qb.branch_hier[0][0]
     else:
         g.assurt((not self.parent_id)
                  or (self.parent_id == qb.branch_hier[1][0]))
     # The user is allowed to set self.last_merge_rid only once: when the
     # branch is created. Once the branch exists, the user can only change
     # last_merge_rid by performing an update.
     # 2013.04.04: And remember to use rid_new and not rid_max.
     if self.last_merge_rid is None:
         self.last_merge_rid = qb.item_mgr.rid_new
     else:
         # NO: Allow update: g.assurt(self.fresh and (self.version == 1))
         if ((self.last_merge_rid < 0)
                 or (self.last_merge_rid > qb.item_mgr.rid_new)):
             raise GWIS_Error('last_merge_rid out-of-bounds: %d' %
                              (self.last_merge_rid, ))
     # We fetch coverage_area as SVG (which is what all the append_* fcns.
     # expect), but PostGIS has no SVG import function (weird, [lb] thinks).
     # 2014.04.27: Rather than just repeat what's in the database for the
     #             prevision branch version (and maybe losing precision since
     #             we fetched coverage_area using conf.db_fetch_precision),
     #             we can recalculate it: see: commit.py calls
     #             byway.Many.branch_coverage_area_update.
     restore_carea = self.coverage_area
     if self.coverage_area.startswith('M '):
         # There's no geometry.svg_polygon_to_xy like there's a svg_line_to_xy,
         # so we use the geometry-type agnostic gml.flat_to_xys rather than
         # just filling in the blanks in geometry.py and writing such a fcn.
         # And this is a little ugly: we have to close the polygon ring, and
         # we have to make it a multipolygon for our geometry translation.
         # (Instead of this dance, we could, e.g.,
         #  ST_AsEWKT(br.coverage_area) AS coverage_area_wkt.)
         # Note that we fetched coverage_area using conf.db_fetch_precision,
         # so we've lost precision, so the caller should make sure to call
         # byway.Many.branch_coverage_area_update if they care (commit does).
         coverage_pgon = gml.flat_to_xys(self.coverage_area)
         coverage_pgon.append(coverage_pgon[0])
         self.coverage_area = geometry.xy_to_ewkt_polygon(
             [
                 coverage_pgon,
             ], precision=conf.postgis_precision)
     self.save_insert(qb, One.item_type_table, One.psql_defns)
     # Restore the geometry to SVG (even though in practive save_core
     # is just used by commit, so the coverage_area is no longer needed,
     # and is about to be recomputed).
     self.coverage_area = restore_carea
Пример #6
0
 def dist_ob_block(self, ob, b):
    geom = gml.flat_to_xys(b.geometry_wkt)
    result = dist_ob_line_segment(ob, geom[0], geom[1])
    for n in range(1, len(geom)-1):
       new_dist = dist_ob_line_segment(ob, geom[n], geom[n+1])
       if new_dist < result:
          result = new_dist
    return result, dist(ob, geom[0]), dist(ob, geom[-1])
Пример #7
0
 def save_core(self, qb):
    item_user_watching.One.save_core(self, qb)
    # A branch's stack ID always the same as its branch ID (self-referential)
    g.assurt(self.stack_id == self.branch_id) # see save_core_get_branch_id
    # NOTE: We only honor create-branch and delete from local requests. From
    #       flashclient, we just support renaming.
    g.assurt(((self.version > 1) and not self.deleted)
             or qb.request_is_local)
    # NOTE: We've already verified the parent ID is valid. If the item is
    # fresh, we've checked that the user has permissions to make a copy of
    # the parent. If not, we've checked the user can edit the branch.
    if self.fresh:
       self.parent_id = qb.branch_hier[0][0]
    else:
       g.assurt((not self.parent_id)
                or (self.parent_id == qb.branch_hier[1][0]))
    # The user is allowed to set self.last_merge_rid only once: when the
    # branch is created. Once the branch exists, the user can only change
    # last_merge_rid by performing an update.
    # 2013.04.04: And remember to use rid_new and not rid_max.
    if self.last_merge_rid is None:
       self.last_merge_rid = qb.item_mgr.rid_new
    else:
       # NO: Allow update: g.assurt(self.fresh and (self.version == 1))
       if ((self.last_merge_rid < 0)
           or (self.last_merge_rid > qb.item_mgr.rid_new)):
          raise GWIS_Error('last_merge_rid out-of-bounds: %d'
                           % (self.last_merge_rid,))
    # We fetch coverage_area as SVG (which is what all the append_* fcns.
    # expect), but PostGIS has no SVG import function (weird, [lb] thinks).
    # 2014.04.27: Rather than just repeat what's in the database for the
    #             prevision branch version (and maybe losing precision since
    #             we fetched coverage_area using conf.db_fetch_precision),
    #             we can recalculate it: see: commit.py calls
    #             byway.Many.branch_coverage_area_update.
    restore_carea = self.coverage_area
    if self.coverage_area.startswith('M '):
       # There's no geometry.svg_polygon_to_xy like there's a svg_line_to_xy,
       # so we use the geometry-type agnostic gml.flat_to_xys rather than
       # just filling in the blanks in geometry.py and writing such a fcn.
       # And this is a little ugly: we have to close the polygon ring, and
       # we have to make it a multipolygon for our geometry translation.
       # (Instead of this dance, we could, e.g.,
       #  ST_AsEWKT(br.coverage_area) AS coverage_area_wkt.)
       # Note that we fetched coverage_area using conf.db_fetch_precision,
       # so we've lost precision, so the caller should make sure to call
       # byway.Many.branch_coverage_area_update if they care (commit does).
       coverage_pgon = gml.flat_to_xys(self.coverage_area)
       coverage_pgon.append(coverage_pgon[0])
       self.coverage_area = geometry.xy_to_ewkt_polygon([coverage_pgon,],
                                       precision=conf.postgis_precision)
    self.save_insert(qb, One.item_type_table, One.psql_defns)
    # Restore the geometry to SVG (even though in practive save_core
    # is just used by commit, so the coverage_area is no longer needed,
    # and is about to be recomputed).
    self.coverage_area = restore_carea
Пример #8
0
def landmarks_polygons(step, qb):
   # idea: get polygons close to end node
   # Currently kind of useless because we don't have terrain names

   # get geometry for end point of this step
   pts = gml.flat_to_xys(step.geometry)
   node_geo = pts[0]
   if (step.forward):
      node_geo = pts[len(pts) - 1]

   sql_str = (
      """
      SELECT
         DISTINCT ON (gia.stack_id) gia.stack_id,
         gia.acl_grouping,
         gia.name,
         gf.geofeature_layer_id,
         gf.geometry AS geometry,
         ST_AsText(gf.geometry) AS geometry_wkt,
         ST_DISTANCE(gf.geometry, ST_SetSRID(ST_Point(%s, %s), %d)) AS dist,
         ST_AsSVG(ST_Scale(gf.geometry, 1, -1, 1), 0, %d) AS geometry_svg

      FROM group_item_access AS gia
         JOIN geofeature AS gf ON (gia.item_id = gf.system_id)

      WHERE
         gia.valid_until_rid = %d
         AND NOT gia.deleted
         AND NOT gia.reverted
         AND gia.branch_id = %d
         AND gia.item_type_id = %d
         AND ST_DISTANCE(gf.geometry, ST_SetSRID(ST_Point(%s, %s), %d)) < %d
         AND gia.group_id = %d
         AND gia.access_level_id <= %d

      ORDER BY gia.stack_id, gia.acl_grouping DESC
      """ % (node_geo[0], node_geo[1], conf.default_srid,
             conf.db_fetch_precision,
             conf.rid_inf,
              qb.branch_hier[0][0],
              Item_Type.TERRAIN,
              node_geo[0], node_geo[1], conf.default_srid,
              Landmark.max_search_distance,
              group.Many.public_group_id(qb.db),
              Access_Level.client,))

   rows = qb.db.sql(sql_str)
   for row in rows:
      # TODO: If namy is none, get terrain type
      log.debug('terrain geometry: ' + str(row['geometry_svg']))
      step.landmarks.append(Landmark(name=row['name'],
                                     item_id=row['stack_id'],
                                     type_id=Item_Type.TERRAIN,
                                     geometry=row['geometry_svg'],
                                     dist=row['dist']))
   return
Пример #9
0
def get_point_index(b, point):
   points = gml.flat_to_xys(b.geometry_wkt)
   dist_best = float('inf')
   i_best = 0
   for i in range(0, len(points)-1):
      dist = dist_point_line(point, points[i], points[i+1])
      if ((dist < dist_best) and (dist is not None)):
         i_best = i
         dist_best = dist
   return i_best + 1
Пример #10
0
def landmarks_big_crossings(step, qb):
    # idea: get road of certain types (e.g. highways) that geometrically cross
    # this step
    # TODO: the intersection logic could be improved

    sql_str = ("""
      SELECT
         DISTINCT ON (gia.stack_id) gia.stack_id,
         gia.acl_grouping,
         gia.name

      FROM group_item_access AS gia
         JOIN geofeature AS gf ON (gia.item_id = gf.system_id)

      WHERE
         gia.valid_until_rid = %d
         AND NOT gia.deleted
         AND NOT gia.reverted
         AND gia.branch_id = %d
         AND gia.item_type_id = %d
         AND ST_Crosses(gf.geometry, '%s'::GEOMETRY)
         AND gf.geofeature_layer_id IN (%d, %d, %d)
         AND gia.group_id = %d
         AND gia.access_level_id <= %d

      ORDER BY gia.stack_id, gia.acl_grouping DESC
      """ % (
        conf.rid_inf,
        qb.branch_hier[0][0],
        Item_Type.BYWAY,
        geometry.xy_to_ewkt_line(gml.flat_to_xys(step.geometry)),
        byway.Geofeature_Layer.Highway,
        byway.Geofeature_Layer.Expressway,
        byway.Geofeature_Layer.Major_Trail,
        group.Many.public_group_id(qb.db),
        Access_Level.client,
    ))

    rows = qb.db.sql(sql_str)
    # Checking a set for membership is faster than using a list.
    name_list = set()
    for row in rows:
        try:
            if (len(row['name']) > 0 and not row['name'] in name_list):
                step.landmarks.append(
                    Landmark(name=row['name'],
                             item_id=row['stack_id'],
                             type_id=Item_Type.BYWAY))
                name_list.add(row['name'])
        except TypeError:
            # row['name'] is None.
            pass
    return
Пример #11
0
def landmarks_pois(step, qb):
    # get geometry for end point of this step
    pts = gml.flat_to_xys(step.geometry)
    node_geo = pts[0]
    if (step.forward):
        node_geo = pts[len(pts) - 1]

    sql_str = ("""
      SELECT
         DISTINCT ON (gia.stack_id) gia.stack_id,
         gia.acl_grouping,
         gia.name,
         gf.geometry AS geometry,
         ST_AsText(gf.geometry) AS geometry_wkt,
         ST_DISTANCE(gf.geometry, ST_SetSRID(ST_Point(%s, %s), %d)) as dist

      FROM group_item_access AS gia
         JOIN geofeature AS gf ON (gia.item_id = gf.system_id)

      WHERE gia.valid_until_rid = %d
        AND NOT gia.deleted
        AND NOT gia.reverted
        AND gia.branch_id = %d
        AND gia.item_type_id = %d
        AND ST_DISTANCE(gf.geometry, ST_SetSRID(ST_Point(%s, %s), %d)) < %d
        AND gia.group_id = %d
        AND gia.access_level_id <= %d

      ORDER BY gia.stack_id, gia.acl_grouping DESC
      """ % (
        node_geo[0],
        node_geo[1],
        conf.default_srid,
        conf.rid_inf,
        qb.branch_hier[0][0],
        Item_Type.WAYPOINT,
        node_geo[0],
        node_geo[1],
        conf.default_srid,
        Landmark.max_search_distance,
        group.Many.public_group_id(qb.db),
        Access_Level.client,
    ))
    #gia.group_id IN (%s) => qb.filters.gia_use_gids?

    rows = qb.db.sql(sql_str)
    for row in rows:
        step.landmarks.append(
            Landmark(name=row['name'],
                     item_id=row['stack_id'],
                     type_id=Item_Type.WAYPOINT,
                     geometry=row['geometry_wkt'],
                     dist=row['dist']))
Пример #12
0
def landmarks_big_crossings(step, qb):
   # idea: get road of certain types (e.g. highways) that geometrically cross
   # this step
   # TODO: the intersection logic could be improved

   sql_str = (
      """
      SELECT
         DISTINCT ON (gia.stack_id) gia.stack_id,
         gia.acl_grouping,
         gia.name

      FROM group_item_access AS gia
         JOIN geofeature AS gf ON (gia.item_id = gf.system_id)

      WHERE
         gia.valid_until_rid = %d
         AND NOT gia.deleted
         AND NOT gia.reverted
         AND gia.branch_id = %d
         AND gia.item_type_id = %d
         AND ST_Crosses(gf.geometry, '%s'::GEOMETRY)
         AND gf.geofeature_layer_id IN (%d, %d, %d)
         AND gia.group_id = %d
         AND gia.access_level_id <= %d

      ORDER BY gia.stack_id, gia.acl_grouping DESC
      """ % (conf.rid_inf,
             qb.branch_hier[0][0],
             Item_Type.BYWAY,
             geometry.xy_to_ewkt_line(gml.flat_to_xys(step.geometry)),
             byway.Geofeature_Layer.Highway,
             byway.Geofeature_Layer.Expressway,
             byway.Geofeature_Layer.Major_Trail,
             group.Many.public_group_id(qb.db),
             Access_Level.client,))

   rows = qb.db.sql(sql_str)
   # Checking a set for membership is faster than using a list.
   name_list = set()
   for row in rows:
      try:
         if (len(row['name']) > 0 and not row['name'] in name_list):
            step.landmarks.append(Landmark(name=row['name'],
                                           item_id=row['stack_id'],
                                           type_id=Item_Type.BYWAY))
            name_list.add(row['name'])
      except TypeError:
         # row['name'] is None.
         pass
   return
Пример #13
0
def landmarks_pois(step, qb):
   # get geometry for end point of this step
   pts = gml.flat_to_xys(step.geometry)
   node_geo = pts[0]
   if (step.forward):
      node_geo = pts[len(pts) - 1]

   sql_str = (
      """
      SELECT
         DISTINCT ON (gia.stack_id) gia.stack_id,
         gia.acl_grouping,
         gia.name,
         gf.geometry AS geometry,
         ST_AsText(gf.geometry) AS geometry_wkt,
         ST_DISTANCE(gf.geometry, ST_SetSRID(ST_Point(%s, %s), %d)) as dist

      FROM group_item_access AS gia
         JOIN geofeature AS gf ON (gia.item_id = gf.system_id)

      WHERE gia.valid_until_rid = %d
        AND NOT gia.deleted
        AND NOT gia.reverted
        AND gia.branch_id = %d
        AND gia.item_type_id = %d
        AND ST_DISTANCE(gf.geometry, ST_SetSRID(ST_Point(%s, %s), %d)) < %d
        AND gia.group_id = %d
        AND gia.access_level_id <= %d

      ORDER BY gia.stack_id, gia.acl_grouping DESC
      """ % (node_geo[0], node_geo[1], conf.default_srid,
             conf.rid_inf,
             qb.branch_hier[0][0],
             Item_Type.WAYPOINT,
             node_geo[0], node_geo[1], conf.default_srid,
             Landmark.max_search_distance,
             group.Many.public_group_id(qb.db),
             Access_Level.client,))
   #gia.group_id IN (%s) => qb.filters.gia_use_gids?

   rows = qb.db.sql(sql_str)
   for row in rows:
      step.landmarks.append(Landmark(name=row['name'],
                                     item_id=row['stack_id'],
                                     type_id=Item_Type.WAYPOINT,
                                     geometry=row['geometry_wkt'],
                                     dist=row['dist']))
Пример #14
0
   def split_block(self, b, intersection, new_node_id=None):
      points = gml.flat_to_xys(b.geometry_wkt)
      index = get_point_index(b, intersection)

      # Insert the new vertex into the byway
      points.insert(index, intersection)

      # Split the byway at the new vertex
      new_split_block = copy.deepcopy(b)
      new_split_block.stack_id = self.get_fresh_id()
      new_split_block.split_from_stack_id = b.stack_id
      geom1 = list()
      geom2 = list()
      # TODO: this can be done without a for loop
      for i in range(0,len(points)):
         if (i <= index):
            geom1.append(points[i])
         if (i >= index):
            geom2.append(points[i])

      if (new_node_id is None):
         new_split_block.nid2 = self.get_fresh_id()
      else:
         new_split_block.nid2 = new_node_id
      b.nid1 = new_split_block.nid2
   
      # Remove the old split block since its geometry has changed
      for block in self.new_byways:
         if block.stack_id == b.stack_id:
            self.new_byways.remove(block)
            break

      # Save the new split blocks to the byways list
      new_split_block.geometry_wkt = (
         'LINESTRING(%s)' % (gml.wkt_coords_format(geom1),))
      self.new_byways.append(new_split_block)
      b.geometry_wkt = (
         'LINESTRING(%s)' % (gml.wkt_coords_format(geom2),))
      self.new_byways.append(b)
      return b.nid1, new_split_block.stack_id, b.stack_id
Пример #15
0
   def proj_block(self, point, block):

      geom_str = ("ST_GeomFromText('%s', %d)"
                  % (block.geometry_wkt,
                     conf.default_srid,))

      rows = self.qb.db.sql(
         """
         SELECT
            ST_AsEWKT(
               ST_Line_Interpolate_Point(
                  %s, ST_Line_Locate_Point(
                        %s, ST_SetSRID(ST_Point(%s, %s), %d)))) AS geom
         """ % (geom_str, geom_str,
                point[0], point[1],
                conf.default_srid,))

      point = geometry.wkt_line_to_xy(rows[0]['geom'])[0]

      # Get block geometry
      block_points = gml.flat_to_xys(block.geometry_wkt)

      return point
Пример #16
0
    def save_rstep(self, qb, route, step_number):

        self.route_id = route.system_id
        # FIXME: Do we need these? They're in the table...
        self.route_stack_id = route.stack_id
        self.route_version = route.version

        self.step_number = step_number

        g.assurt(self.byway_stack_id > 0)
        g.assurt(self.byway_version > 0)
        # 2014.08.19: flashclient sending byway_id="0" ???
        if (not self.byway_id) or (self.byway_id <= 0):
            if route.stack_id not in One.warned_re_sysid_stk_ids:
                One.warned_re_sysid_stk_ids.add(route.stack_id)
                log.warning(
                    'save_rstep: byway_id not sent from client: route sid: %s'
                    % (route.stack_id, ))
            branch_ids = [str(branch_tup[0]) for branch_tup in qb.branch_hier]
            # FIXME: The ORDER BY is pretty lame... but if we just have a stack ID
            #        and a version, and if we don't check against the time when
            #        the route was requested, we can't know for sure which branch
            #        the byway is from (e.g., a route was requested in a leafy
            #        branch and the parent branch's byways were selected, but
            #        then one byway is edited in both the parent and the leaf,
            #        then there are two byways with the same stack ID and version
            #        but different branch_ids: since our qb is Current but the
            #        route is historic, we would really need to find the revision
            #        when the route was requested and use that to find the true
            #        byway system_id). This code just finds the leafiest matching
            #        byway... which is probably okay, since this code is for
            #        saving byways, and most users will be saving to the basemap
            #        branch (who uses the MetC Bikeways 2012 branch, anyway? No
            #        one...), and also this code is just a stopgap until we fix
            #        the real problem that is the client is not sending byway
            #        system IDs, which might be a pyserver problem not sending
            #        them to the client in the first place....
            sys_id_sql = ("""
            SELECT iv.system_id
              FROM item_versioned AS iv
             WHERE iv.stack_id = %d
               AND iv.version = %d
               AND branch_id IN (%s)
             ORDER BY branch_id DESC
             LIMIT 1
            """ % (
                self.byway_stack_id,
                self.byway_version,
                ','.join(branch_ids),
            ))
            rows = qb.db.sql(sys_id_sql)
            g.assurt(
                len(rows) == 1)  # Or not, if no match, which would be weird.
            self.byway_id = rows[0]['system_id']
            g.assurt(self.byway_id > 0)

        if (self.geometry and (self.travel_mode == Travel_Mode.transit)):
            # Old CcpV1: db_glue automatically prepends the SRID for columns named
            # 'geometry' but we have to do it manually here so constraints pass.
            # 2012.09.27: What does 'constraints pass' mean? Doesn't db_glue fix
            #             this list the comment says?
            # MAYBE: This feels like a gml fcn. See maybe: wkt_linestring_get.
            wkt_geom = (
                'SRID=%s;LINESTRING(%s)' % (
                    conf.default_srid,
                    gml.wkt_coords_format(
                        # FIXED?: Was: gml.flat_to_xys(self.geometry[2:])
                        gml.flat_to_xys(self.geometry)),
                ))
        else:
            # Either this is a byway-associated route_step, so we don't save the
            # byway's geometry (it's easy to lookup in the database), or something
            # else...
            wkt_geom = None
            if self.travel_mode == Travel_Mode.transit:
                # FIXME: Does this mean we're clearing existing geometry? Seems
                #        weird...
                log.warning(
                    'save_rstep: transit step has geometry? are we clearing it?'
                )

        # FIXME: What about wkt_geom? Is this right?
        self.transit_geometry = wkt_geom

        self.save_insert(qb, One.item_type_table, One.psql_defns)
Пример #17
0
def landmarks_graph_properties(step, qb):
   # idea: detect T intersections

   pts = gml.flat_to_xys(step.geometry)
   step_xys = (pts[0], pts[1])
   node_id = step.beg_node_id
   if (step.forward):
      step_xys = (pts[len(pts)-1], pts[len(pts)-2])
      node_id = step.fin_node_id

   # find byways connected to this node

   sql_str = (
      """
      SELECT
         DISTINCT ON (gia.stack_id) gia.stack_id,
         gia.acl_grouping,
         ST_AsText(gf.geometry) AS geometry_wkt,
         gf.beg_node_id

      FROM group_item_access AS gia
         JOIN geofeature AS gf ON (gia.item_id = gf.system_id)
         JOIN node_byway ON (node_byway.byway_stack_id = gia.stack_id)

      WHERE
         gia.valid_until_rid = %d
         AND NOT gia.deleted
         AND NOT gia.reverted
         AND gia.branch_id = %d
         AND gia.item_type_id = %d
         AND node_byway.branch_id = %d
         AND node_byway.node_stack_id = %d
         AND NOT gia.stack_id = %s
         AND gia.group_id = %d
         AND gia.access_level_id <= %d

      ORDER BY gia.stack_id, gia.acl_grouping DESC
      """ % (conf.rid_inf,
             qb.branch_hier[0][0],
             Item_Type.BYWAY,
             qb.branch_hier[0][0],
             node_id,
             step.byway_stack_id,
             group.Many.public_group_id(qb.db),
             Access_Level.client,))

   rows = qb.db.sql(sql_str)

   # If not two, exit (no T intersection here)
   if not len(rows) == 2:
      return

   # get first two points of each byway
   byways_xys = list()
   for row in rows:
      xys = gml.flat_to_xys(row['geometry_wkt'])
      if (row['beg_node_id'] == node_id):
         byways_xys.append((xys[0], xys[1],))
      else:
         byways_xys.append((xys[len(xys)-1], xys[len(xys)-2],))

   # now we can calculate all angles, if the other two byways are almost
   # straight and they are both at least between 60 and 120 degrees from
   # this one

   angle1 = geometry.v_dir(step_xys[0], step_xys[1]) * 180 / math.pi
   angle2 = geometry.v_dir(byways_xys[0][0], byways_xys[0][1]) * 180 / math.pi
   angle3 = geometry.v_dir(byways_xys[1][0], byways_xys[1][1]) * 180 / math.pi

   # Check that agles are at 90+-30 degrees
   dif1 = angle1 - angle2
   if not ((abs(dif1) > 60 and abs(dif1) < 120)
       or (abs(dif1) > 240 and abs(dif1) < 300)):
      return
   dif2 = angle1 - angle3
   if not ((abs(dif2) > 60 and abs(dif2) < 120)
       or (abs(dif2) > 240 and abs(dif2) < 300)):
      return
   if (abs(dif2 - dif1) < 90):
      # angles are too close, probably perpendicular toward the same side
      return

   # We found a T intersection
   step.landmarks.append(Landmark(name='',
                                  item_id=-1,
                                  type_id=Item_Type.LANDMARK_T))
Пример #18
0
   def continue_block(self, new_block, direction, last_block_ids=None,
                      ignore=None):
      if direction == -1:
         endpoint_index = 0
      else:
         endpoint_index = -1

      extending_points = gml.flat_to_xys(new_block.geometry_wkt)
      
      if ignore is None:
         ignore = [new_block.stack_id]
      # If there are no blocks nearby, no need to extend this block
      nearest_block = self.get_nearest_block(
         extending_points[endpoint_index],
         last_block_ids,
         ignore=ignore)
      if (nearest_block is None
          or nearest_block.dist > self.wtem.distance_error):
         return None
      ignore_ids = [nearest_block.stack_id]

      near_points = gml.flat_to_xys(nearest_block.geometry_wkt)

      # If we are close to an endpoint of another block, connect to it.
      if (dist(extending_points[endpoint_index], near_points[0])
          <= self.wtem.distance_error):
         # add blocks that connect to that endpoint to ignore list for next
         # block continuation
         ignore_ids.extend(
            [b.stack_id for b in self.get_connected_blocks(
               nearest_block, nearest_block.nid1)])
         if direction == -1:
            extending_points[0] = near_points[0]
            new_block.nid1 = nearest_block.nid1
         else:
            extending_points[-1] = near_points[0]
            new_block.nid2 = nearest_block.nid1
      elif (dist(extending_points[endpoint_index], near_points[-1])
            <= self.wtem.distance_error):
         # add blocks that connect to that endpoint to ignore list for next
         # block continuation
         ignore_ids.extend(
            [b.stack_id for b in self.get_connected_blocks(
               nearest_block, nearest_block.nid2)])
         if direction == -1:
            extending_points[0] = near_points[-1]
            new_block.nid1 = nearest_block.nid2
         else:
            extending_points[-1] = near_points[-1]
            new_block.nid2 = nearest_block.nid2
      else:
         # Get location of closest point on the nearest block and the expected
         # index in that block's sequence of points.
         point = self.proj_block(
            extending_points[endpoint_index], nearest_block)

         new_node, b1_id, b2_id = self.split_block(nearest_block, point)
         ignore_ids = [b1_id, b2_id]

         # Connect the current block to the intersection
         if direction == -1:
            extending_points.insert(0, point)
            new_block.nid1 = new_node
         else:
            extending_points.append(point)
            new_block.nid2 = new_node
      
      new_block.geometry_wkt = (
         'LINESTRING(%s)' % (gml.wkt_coords_format(extending_points),))
      return ignore_ids
Пример #19
0
def landmarks_polygons(step, qb):
    # idea: get polygons close to end node
    # Currently kind of useless because we don't have terrain names

    # get geometry for end point of this step
    pts = gml.flat_to_xys(step.geometry)
    node_geo = pts[0]
    if (step.forward):
        node_geo = pts[len(pts) - 1]

    sql_str = ("""
      SELECT
         DISTINCT ON (gia.stack_id) gia.stack_id,
         gia.acl_grouping,
         gia.name,
         gf.geofeature_layer_id,
         gf.geometry AS geometry,
         ST_AsText(gf.geometry) AS geometry_wkt,
         ST_DISTANCE(gf.geometry, ST_SetSRID(ST_Point(%s, %s), %d)) AS dist,
         ST_AsSVG(ST_Scale(gf.geometry, 1, -1, 1), 0, %d) AS geometry_svg

      FROM group_item_access AS gia
         JOIN geofeature AS gf ON (gia.item_id = gf.system_id)

      WHERE
         gia.valid_until_rid = %d
         AND NOT gia.deleted
         AND NOT gia.reverted
         AND gia.branch_id = %d
         AND gia.item_type_id = %d
         AND ST_DISTANCE(gf.geometry, ST_SetSRID(ST_Point(%s, %s), %d)) < %d
         AND gia.group_id = %d
         AND gia.access_level_id <= %d

      ORDER BY gia.stack_id, gia.acl_grouping DESC
      """ % (
        node_geo[0],
        node_geo[1],
        conf.default_srid,
        conf.db_fetch_precision,
        conf.rid_inf,
        qb.branch_hier[0][0],
        Item_Type.TERRAIN,
        node_geo[0],
        node_geo[1],
        conf.default_srid,
        Landmark.max_search_distance,
        group.Many.public_group_id(qb.db),
        Access_Level.client,
    ))

    rows = qb.db.sql(sql_str)
    for row in rows:
        # TODO: If namy is none, get terrain type
        log.debug('terrain geometry: ' + str(row['geometry_svg']))
        step.landmarks.append(
            Landmark(name=row['name'],
                     item_id=row['stack_id'],
                     type_id=Item_Type.TERRAIN,
                     geometry=row['geometry_svg'],
                     dist=row['dist']))
    return
Пример #20
0
def landmarks_graph_properties(step, qb):
    # idea: detect T intersections

    pts = gml.flat_to_xys(step.geometry)
    step_xys = (pts[0], pts[1])
    node_id = step.beg_node_id
    if (step.forward):
        step_xys = (pts[len(pts) - 1], pts[len(pts) - 2])
        node_id = step.fin_node_id

    # find byways connected to this node

    sql_str = ("""
      SELECT
         DISTINCT ON (gia.stack_id) gia.stack_id,
         gia.acl_grouping,
         ST_AsText(gf.geometry) AS geometry_wkt,
         gf.beg_node_id

      FROM group_item_access AS gia
         JOIN geofeature AS gf ON (gia.item_id = gf.system_id)
         JOIN node_byway ON (node_byway.byway_stack_id = gia.stack_id)

      WHERE
         gia.valid_until_rid = %d
         AND NOT gia.deleted
         AND NOT gia.reverted
         AND gia.branch_id = %d
         AND gia.item_type_id = %d
         AND node_byway.branch_id = %d
         AND node_byway.node_stack_id = %d
         AND NOT gia.stack_id = %s
         AND gia.group_id = %d
         AND gia.access_level_id <= %d

      ORDER BY gia.stack_id, gia.acl_grouping DESC
      """ % (
        conf.rid_inf,
        qb.branch_hier[0][0],
        Item_Type.BYWAY,
        qb.branch_hier[0][0],
        node_id,
        step.byway_stack_id,
        group.Many.public_group_id(qb.db),
        Access_Level.client,
    ))

    rows = qb.db.sql(sql_str)

    # If not two, exit (no T intersection here)
    if not len(rows) == 2:
        return

    # get first two points of each byway
    byways_xys = list()
    for row in rows:
        xys = gml.flat_to_xys(row['geometry_wkt'])
        if (row['beg_node_id'] == node_id):
            byways_xys.append((
                xys[0],
                xys[1],
            ))
        else:
            byways_xys.append((
                xys[len(xys) - 1],
                xys[len(xys) - 2],
            ))

    # now we can calculate all angles, if the other two byways are almost
    # straight and they are both at least between 60 and 120 degrees from
    # this one

    angle1 = geometry.v_dir(step_xys[0], step_xys[1]) * 180 / math.pi
    angle2 = geometry.v_dir(byways_xys[0][0], byways_xys[0][1]) * 180 / math.pi
    angle3 = geometry.v_dir(byways_xys[1][0], byways_xys[1][1]) * 180 / math.pi

    # Check that agles are at 90+-30 degrees
    dif1 = angle1 - angle2
    if not ((abs(dif1) > 60 and abs(dif1) < 120) or
            (abs(dif1) > 240 and abs(dif1) < 300)):
        return
    dif2 = angle1 - angle3
    if not ((abs(dif2) > 60 and abs(dif2) < 120) or
            (abs(dif2) > 240 and abs(dif2) < 300)):
        return
    if (abs(dif2 - dif1) < 90):
        # angles are too close, probably perpendicular toward the same side
        return

    # We found a T intersection
    step.landmarks.append(
        Landmark(name='', item_id=-1, type_id=Item_Type.LANDMARK_T))
Пример #21
0
   def do_conflation(self):
      '''Viterbi algorithm'''

      now = time.time()
      self.stage_initialize('Conflating track')

      # Observations (track points)
      obs = [(float(tp.x), float(tp.y), tp.timestamp,)
             for tp in self.track.track_points]
      self.sanitize(obs)
      self.fetch_byways(obs)

      # List of dicts mapping an observation index and a byway step to the
      # composite probability that observation came from that byway
      V = [{}]
      # List of dicts mapping observation index and byway ID to the most likely
      # path (list of Steps) leading to that byway in the current iteration
      path = [{}]
      # List of lists giving the IDs of the byway visited at each step.
      visited_ids = [[]]

      # Get nearby blocks
      nearby_byways = self.get_nearby_blocks(obs[0])
      # If there are no nearby blocks, create one.
      if (len(nearby_byways) == 0):
         bounds = self.new_block_bounds(obs, 0)
         block = self.create_new_block(obs, bounds)
         nearby_byways.append(block)

      # We use this to keep track of which is the current best result
      # (byway_meta, probability)
      max_result = [(None, 0,),]

      def initialize(blocks):
         # Initialize when t == 0
         for b in blocks:
            # one for each direction (forward and backward)
            V[0]['f' + str(b.stack_id)] = self.emit_p(b, obs[0])
            V[0]['b' + str(b.stack_id)] = self.emit_p(b, obs[0])
            visited_ids[0].append(b.stack_id)
            path[0]['f' + str(b.stack_id)] = [Step(b, b.nid1, b.nid2),]
            path[0]['b' + str(b.stack_id)] = [Step(b, b.nid2, b.nid1),]
            max_result[0] = (b, V[0]['f' + str(b.stack_id)],)

      initialize(nearby_byways)

      # Run Viterbi for t > 0
      progress = 0
      t = 1
      while t < len(obs):
         # Latest entry in V maps blocks to their probability of being the true
         # location of this observation
         V.append({})
         path.append({})
         visited_ids.append(list())
         current_prog = math.floor(t * 100 / len(obs))
         if (current_prog > progress):
            progress = current_prog
            self.stage_work_item_update(stage_progress=progress)
         
         # Get the blocks that could have produced this observation
         nearby_blocks = self.get_nearby_blocks(obs[t])
         candidate_steps = list()
         # For each nearby block, add it to candidate_blocks if it is connected
         # to the end of any of the possible paths from the previous iteration
         for y in nearby_blocks:
            step = Step(y, y.nid1, y.nid2)
            for prev_id in V[t-1].keys():
               if self.trans_p(path[t-1][prev_id][-1], step) > 0:
                  candidate_steps.append(step)
                  break
            step = Step(y, y.nid2, y.nid1)
            for prev_id in V[t-1].keys():
               if self.trans_p(path[t-1][prev_id][-1], step) > 0:
                  candidate_steps.append(step)
                  break

         # If there are no nearby blocks, or all nearby blocks have a
         # transition probability of 0, create a new block and rewind Viterbi
         # to the first observation where the new block or connected blocks had
         # any emission probability
         if (len(candidate_steps) == 0):
            bounds = self.new_block_bounds(obs, t, visited_ids[t-1])
            blocks = list()
            fixed = False
            if (bounds[0] == bounds[1]):
               main_ob = obs[bounds[0]]
               # all we need is to create an intersection
               nearby = self.get_nearby_blocks(main_ob,
                                               self.wtem.distance_error)
               # Check if there are endpoints of new blocks nearby, If so,
               # extend and connect
               for b in nearby:
                  if (not b.stack_id in visited_ids[t-1]):
                     points = gml.flat_to_xys(b.geometry_wkt)
                     if (dist(main_ob, points[0])
                        < self.wtem.distance_error):
                        self.continue_block(b, -1)
                        fixed = True
                     elif (dist(main_ob, points[-1])
                        < self.wtem.distance_error):
                        self.continue_block(b, 1)
                        fixed = True
                     elif (self.create_intersection(b, main_ob)):
                        fixed = True
                        break
                     else:
                        continue
                     # Remove the old split block since its geometry has changed
                     for bwy in self.new_byways:
                        if bwy.stack_id == b.stack_id:
                           self.new_byways.remove(bwy)
                           break
                     # save modified block
                     self.new_byways.append(b)
               blocks.extend(self.get_connected_blocks(
                  self.get_nearest_block(main_ob,visited_ids[t-1])))
            if (not fixed):
               new_block = self.create_new_block(
                              obs, bounds, visited_ids[t-1])

               blocks = self.get_connected_blocks(new_block)
               blocks.append(new_block)
            emission = False
            for index in range(0, len(obs)-1):
               for b in blocks:
                  if (self.dist_ob_block(obs[index], b)[0]
                      < self.wtem.cutoff_distance):
                     emission = True
                     break
               if (emission):
                  break
            t = index
            # If rewinding to the beginning, reset the data structures
            if t > 0:
               del V[t:]
               del path[t:]
               del max_result[t:]
               del visited_ids[t:]
            else:
               V = [{}]
               path = [{}]
               max_result = [(None, 0,),]
               visited_ids = [[]]
               initialize(self.get_nearby_blocks(obs[0]))
               t = 1
            continue

         fix_bool = True
         max_last = (None, 0,)
      
         # Iterate over all possible blocks for this observation
         for candidate in candidate_steps:
            direction = 'f'
            if (candidate.forward_node == candidate.bywaymeta.nid1):
               direction = 'b'
            candidate_id = direction + str(candidate.bywaymeta.stack_id)

            # Select the block that most probably would have preceded this
            # block and the probability
            # of that path
            (prob, state,) = max(
               [(V[t-1][prev_id]
                 * self.trans_p(path[t-1][prev_id][-1], candidate)
                 * self.emit_p(candidate.bywaymeta, obs[t]),
                 prev_id,)
                for prev_id in V[t-1].keys()])

            # Save the probability of this block if higher than 0
            if (prob > 0):
               V[t][candidate_id] = prob
               # Keep track of the last really close blocks at this point
               if ((not candidate.bywaymeta.stack_id in visited_ids[t])
                   and (candidate.bywaymeta.dist <= self.wtem.distance_error)):
                  visited_ids[t].append(candidate.bywaymeta.stack_id)

            # Save the most likely path leading to this block
            path[t][candidate_id] = path[t-1][state] + [candidate]

            if (prob > max_last[1]):
               max_last = (candidate_id, prob)
            if (prob > (10**-20)):
               fix_bool = False
      
         if (len(visited_ids[t]) == 0) and (t > 0):
            visited_ids[t] = visited_ids[t-1]

         if (max_last[1] > 0):
            max_result.append(max_last)
         else:
            max_result.append(max_result[t-1])
      
         # If all values are too small, make bigger (does not affect
         # final results, which are used for comparison and not in an
         # absolute way)
         if fix_bool and (max_last[1] > 0):
            multiplier = round(1 / max_last[1])
            for k in V[t].keys():
               V[t][k] = V[t][k] * multiplier

         # Break if the algorithm did not find any possible blocks (should not
         # happen if we are adding new blocks)
         if (max_last[1] <= 0):
            break
         t += 1

      log.debug('Finished Viterbi!')
      log.debug('Time for conflation: %s' % (str(time.time() - now),))
      self.final_path = path[t-1][max_result[-1][0]]
      self.obs = obs
      self.stage_work_item_update(stage_progress=100)
   def fetch_n_save(self):
      command.Op_Handler.fetch_n_save(self)
      if ((not self.req.client.username)
          or (self.req.client.username == conf.anonymous_username)):
         raise GWIS_Error('User must be logged in.')
      else:
         # 'erase' previous landmarks
         sql = (
            """
            UPDATE landmark_exp_landmarks
            SET current = '%s'
            WHERE username = %s
              AND route_system_id = %d
            """) % ('f',
                    self.req.db.quoted(self.req.client.username),
                    self.route_system_id,)
         self.req.db.transaction_begin_rw()
         self.req.db.sql(sql)
         self.req.db.transaction_commit()

         for l in self.landmarks:
            if l.geometry:
               xys = gml.flat_to_xys(l.geometry)
               if len(xys) > 2:
                  l.geometry = "SRID=%s;LINESTRING(%s)" % (conf.default_srid,
                                                           l.geometry,)
               else:
                  l.geometry = "SRID=%s;POINT(%s)" % (conf.default_srid,
                                                      l.geometry,)
            sql = """
            INSERT INTO landmark_exp_landmarks
               (username,
                route_system_id,
                landmark_id,
                landmark_type_id,
                landmark_name,
                landmark_geo,
                step_number,
                created)
            VALUES
               (%s, %s, %s, %s, %s, %s, %s, now())
            """
            self.req.db.transaction_begin_rw()
            self.req.db.sql(sql,
               (self.req.db.quoted(self.req.client.username),
                self.route_system_id,
                l.item_id,
                l.type_id,
                l.name,
                l.geometry,
                l.step_number,))
            self.req.db.transaction_commit()

         sql = (
            """
            UPDATE landmark_exp_route
            SET done = 't',
                last_modified = now()
            WHERE username = %s
              AND route_system_id = %d
            """) % (self.req.db.quoted(self.req.client.username),
                    self.route_system_id,)
         self.req.db.transaction_begin_rw()
         self.req.db.sql(sql)
         self.req.db.transaction_commit()