Exemple #1
0
    def matchAlgo2(self, specialOne, qtree):

        bs = specialOne.getBounds()

        TL = Point(lon=bs.leftObj.lon, lat=bs.topObj.lat)
        BR = Point(lon=bs.rightObj.lon, lat=bs.bottomObj.lat)

        # Now shift the points by the threshold
        d = self.match_threshold_metres
        TL.move(-d, d)
        BR.move(d, -d)

        bs_ex = Bounds()
        bs_ex.registerPoint(TL)
        bs_ex.registerPoint(BR)

        #        if not bs.overlaps(bt):
        #            self.log.i("No overlap, skipping")
        #            return (0, 0, 0)

        # Do a query on the qtree for all points in the range of the track
        #        qtreeSub = qtree.queryRangeDebug(bs_ex)
        qtreeSub = qtree.queryRange(bs_ex)

        hit = miss = 0
        waypoints = []

        # Walk through the match track looking for the nearest point in other the track
        for point in specialOne.points_virtual(interpolate_thres_lower_metres,
                                               interpolate_thres_upper_metres):

            # min is the nearest waypoint, but may be be way of range
            min, found = point.findNearest4(qtreeSub)

            self.prePoint(point, found or min)

            d, wp = (None, None)

            # Make sure the point really is in range before calling foundPoint.
            # findNearest4 will always return something even if it is miles away
            if found:
                self.foundPoint(point, found)
                d, wp = found[0]

            if d and d < self.match_threshold_metres:
                hit += 1
##                waypoints.append(WayPoint(copyFrom=pt, name="hit %d" % hit, sym="Dot"))
            else:
                miss += 1


##                waypoints.append(WayPoint(copyFrom=pt, name="miss %d" % miss, sym="Anchor"))

##        self.gpx.writeItem(track)
##        for wp in waypoints:
##            self.gpx.writeItem(wp)

        match_pct = hit / float(hit + miss) * 100

        return (hit, miss, match_pct)
Exemple #2
0
    def __init__(self,
                 bounds,
                 size=5):  # no idea what makes good max size before split
        self.northWest = self.northEast = self.southWest = self.southEast = None
        self.bounds = bounds  # This acts like a filter. Only points within this range are allowed

        # OPT could also store the bounds that the points actually cover, as opposed to the total size of the box we are covering.
        # Each segment keeps track of the bounds of the points it contains
        self.bounds_of_my_points = Bounds()

        self.size = size

        self.segment = self.newSegment()

        # TODO can we hold our segment plus the other 4 in a Segments? Or will this just be too confusing...
        # it would help compatibility?

        self.isSplit = False
Exemple #3
0
    def queryPoint(self, p, distance_metres=10):
        """
        Query the points around a given point
        """
        bounds = Bounds()

        p1 = Point(copyFrom=p)
        p2 = Point(copyFrom=p)

        # Top right
        p1.move(distance_metres, distance_metres)

        # Bottom left
        p2.move(-distance_metres, -distance_metres)

        bounds.registerPoint(p1)
        bounds.registerPoint(p2)

        return self.queryRange(bounds)
Exemple #4
0
    def __init__(self):
        super(WayPointGatherer, self).__init__()

        # Note if we used smaller bounds we would automatically exclude points for countries miles away
        # TODO OPT read the tracks first, work out the (extended) bounds and use for these bounds   
        bounds = Bounds()
        bounds.registerPoint(Point(-90, -90))
        bounds.registerPoint(Point(90, 90))

        # Use a quad tree to store the points
        self.qtree = SegmentsQTree(bounds, 1000)

        self.distanceThreshold = 300
Exemple #5
0
class SegmentsQTree(object):
    """This interface should be similar enough to be used in place of Segments hopefully.."""
    def __init__(self,
                 bounds,
                 size=5):  # no idea what makes good max size before split
        self.northWest = self.northEast = self.southWest = self.southEast = None
        self.bounds = bounds  # This acts like a filter. Only points within this range are allowed

        # OPT could also store the bounds that the points actually cover, as opposed to the total size of the box we are covering.
        # Each segment keeps track of the bounds of the points it contains
        self.bounds_of_my_points = Bounds()

        self.size = size

        self.segment = self.newSegment()

        # TODO can we hold our segment plus the other 4 in a Segments? Or will this just be too confusing...
        # it would help compatibility?

        self.isSplit = False

    def __str__(self):
        return "self: %s" % (self.bounds.__str__())

    def newSegment(self):
        """Maybe this can be overriden to use a segment of a different type"""
        return Segment()

    def getBounds(self):
        return self.bounds

    def addPoint(self, point):

        #        log.i("%s" % self)
        #        log.i("Trying to addPoint: %s" % point)
        if not self.bounds.contains(point):
            #            log.i("out of bounds")
            # Out of bounds
            return False

        reallocate = False

        self.bounds_of_my_points.registerPoint(point)

        if not self.isSplit:
            #            log.i("have %d items already" % self.segment.count())
            if self.segment.count() < self.size:
                #                log.i("adding point to the quad")
                self.segment.addPoint(point)
                return True
#           log.i("quad is full")
            self.isSplit = self.subDivide()


#            reallocate = True

#        log.i("addPoint into subquad")
        ok = self.northWest.addPoint(point) or self.northEast.addPoint(point) or \
            self.southWest.addPoint(point) or self.southEast.addPoint(point)

        if not ok:
            # This can't happen
            log.i("Failed to addPoint: %s" % point)
            log.i("%s" % self)

            log.i("NW: %s\nNE: %s\nSW: %s\nSE: %s" %
                  (self.northWest, self.northEast, self.southWest,
                   self.southEast))

        assert (ok)

        # This optimisation makes it 4x slower according to my benchmark
        # If doing lots of queries maybe it would be worth it.
        #        # Now reallocate the ones at this level into the children.
        #        if reallocate:
        #            for point in self.segment:
        #                self.addPoint(point)
        #
        #                # Clear the points of this segment. Have all be reallocated in the children.
        #                self.segment.points = []

        return ok

    def subDivide(self):
        """Divide this region into four equal parts."""

        log.dbg("/")

        L = self.bounds.left().lon
        R = self.bounds.right().lon
        T = self.bounds.top().lat
        B = self.bounds.bottom().lat

        MID = Point((R - L) / float(2), (T - B) / float(2))

        # NW
        bounds = Bounds()
        bounds.registerPoint(MID)
        bounds.registerPoint(Point(L, T))
        self.northWest = SegmentsQTree(bounds, self.size)

        # NE
        bounds = Bounds()
        bounds.registerPoint(MID)
        bounds.registerPoint(Point(R, T))
        self.northEast = SegmentsQTree(bounds, self.size)

        # SW
        bounds = Bounds()
        bounds.registerPoint(MID)
        bounds.registerPoint(Point(L, B))
        self.southWest = SegmentsQTree(bounds, self.size)

        # SE
        bounds = Bounds()
        bounds.registerPoint(MID)
        bounds.registerPoint(Point(R, B))
        self.southEast = SegmentsQTree(bounds, self.size)

        # We don't bother reallocating the ones at this level into the children.
        # we just have to search current level before children

        return True

    # Would be nice if this could be an iterator, but don't think you can
    # have a recursive iterator
    # TODO Shouldn't this return a Segment type? Or even a qtree type?
    # it seems awful primitive to be doing linear searches on the results
    # perhaps we could sort the result of a query for faster searching.
    # or sort on own values on demand. for fast lookups, or something.
    def queryRange(self, bounds):

        ret_list = []

        if not self.bounds.overlaps(bounds):
            return ret_list

        # Check points in this quad (not children)
        for point in self.segment:
            if bounds.contains(point):
                ret_list.append(point)

        # Recursively check children
        if self.isSplit:
            ret_list += self.northWest.queryRange(bounds)
            ret_list += self.northEast.queryRange(bounds)
            ret_list += self.southWest.queryRange(bounds)
            ret_list += self.southEast.queryRange(bounds)

        return ret_list

    def queryRangeDebug(self, bounds):
        log.i("Querying points in bounds:%s" % bounds)
        lst = self.queryRange(bounds)
        log.i("Found %d points" % len(lst))
        for l in lst:
            print(l)
        return lst

    def queryRangeDebugPrint(self, p1, p2):
        bounds = Bounds()
        bounds.registerPoint(p1)
        bounds.registerPoint(p2)
        return self.queryRangeDebug(bounds)

    def queryPoint(self, p, distance_metres=10):
        """
        Query the points around a given point
        """
        bounds = Bounds()

        p1 = Point(copyFrom=p)
        p2 = Point(copyFrom=p)

        # Top right
        p1.move(distance_metres, distance_metres)

        # Bottom left
        p2.move(-distance_metres, -distance_metres)

        bounds.registerPoint(p1)
        bounds.registerPoint(p2)

        return self.queryRange(bounds)
Exemple #6
0
 def queryRangeDebugPrint(self, p1, p2):
     bounds = Bounds()
     bounds.registerPoint(p1)
     bounds.registerPoint(p2)
     return self.queryRangeDebug(bounds)
Exemple #7
0
    def subDivide(self):
        """Divide this region into four equal parts."""

        log.dbg("/")

        L = self.bounds.left().lon
        R = self.bounds.right().lon
        T = self.bounds.top().lat
        B = self.bounds.bottom().lat

        MID = Point((R - L) / float(2), (T - B) / float(2))

        # NW
        bounds = Bounds()
        bounds.registerPoint(MID)
        bounds.registerPoint(Point(L, T))
        self.northWest = SegmentsQTree(bounds, self.size)

        # NE
        bounds = Bounds()
        bounds.registerPoint(MID)
        bounds.registerPoint(Point(R, T))
        self.northEast = SegmentsQTree(bounds, self.size)

        # SW
        bounds = Bounds()
        bounds.registerPoint(MID)
        bounds.registerPoint(Point(L, B))
        self.southWest = SegmentsQTree(bounds, self.size)

        # SE
        bounds = Bounds()
        bounds.registerPoint(MID)
        bounds.registerPoint(Point(R, B))
        self.southEast = SegmentsQTree(bounds, self.size)

        # We don't bother reallocating the ones at this level into the children.
        # we just have to search current level before children

        return True
Exemple #8
0
        # Top right
        p1.move(distance_metres, distance_metres)

        # Bottom left
        p2.move(-distance_metres, -distance_metres)

        bounds.registerPoint(p1)
        bounds.registerPoint(p2)

        return self.queryRange(bounds)

if __name__ == "__main__":
    """Just some test code to test the QTree"""

    # Cover the world
    bounds = Bounds()
    #    bounds.registerPoint(Point(-90,-90))
    #    bounds.registerPoint(Point(90, 90))
    bounds.registerPoint(Point(0, 0))
    bounds.registerPoint(Point(10, 10))

    sqt = SegmentsQTree(bounds, 5)

    for i in range(0, 10):
        p = Point(i, i)
        sqt.addPoint(p)

    # Add a duplicate point
    sqt.addPoint(Point(5, 5))

    sqt.queryRangeDebugPrint(Point(0, 0), Point(20, 20))
Exemple #9
0
    def endElement(self, name):

        if self.content:
            #            print("have full content of type", type(self.content), self.content)
            content = self.content.strip()
#            content = self.content.strip().encode('ascii', 'ignore')  # this screws up on python3
#            print("have full content ENCODED of type", type(content), content)
        else:
            content = None

        obj = self.selectObj()

        # Handle the content

        if self.location[:2] == ["gpx", "trk"]:
            if self.location == ["gpx", "trk"]:
                [observer.nextTrack(self.track) for observer in self.observers]
                self.track = None

            elif self.location == ["gpx", "trk", "name"]:
                obj.name = content

            elif self.location == ["gpx", "trk", "type"]:
                obj.type = content

            elif self.location == ["gpx", "trk", "trkseg"]:
                [
                    observer.nextTrackSegment(self.track.currentSegment)
                    for observer in self.observers
                ]
                self.track.endSegment()

            elif self.location == ["gpx", "trk", "trkseg", "trkpt"]:
                self.track.addPoint(self.tp)
                [
                    observer.nextTrackPoint(self.tp)
                    for observer in self.observers
                ]
                self.tp = None

            elif self.location == [
                    "gpx", "trk", "trkseg", "trkpt", "extensions",
                    "gpxtpx:TrackPointExtension", "gpxtpx:hr"
            ]:
                self.tp.hr = int(content)

            elif self.location == [
                    "gpx", "trk", "trkseg", "trkpt", "extensions",
                    "gpxtpx:TrackPointExtension", "gpxtpx:cad"
            ]:
                self.tp.cad = int(content)

            elif self.location == ["gpx", "trk", "trkseg", "trkpt", "time"]:

                #                print("yContent is type", type(content))
                #                print("yContent value is", content)

                self.tp.setTime(content)

            elif self.location == ["gpx", "trk", "trkseg", "trkpt", "ele"]:
                if content:
                    #                    print("xContent is type", type(content))
                    #                    print("xContent value is", content)
                    self.tp.elevation = float(content)

        elif self.location[:2] == ['gpx', 'wpt']:

            if self.location == ['gpx', 'wpt']:
                [
                    observer.nextWayPoint(self.wayPoint)
                    for observer in self.observers
                ]
                self.wayPoint = None

            elif self.location == ["gpx", "wpt", "name"]:
                obj.name = content

            elif self.location == ["gpx", "wpt", "cmt"]:
                obj.comment = content
            else:
                pass

        elif self.location[:2] == ['gpx', 'rte']:
            if self.location == ['gpx', 'rte']:
                [observer.nextRoute(self.route) for observer in self.observers]
                self.route = None

            elif self.location == ['gpx', 'rte', 'name']:
                self.route.name = content

            elif self.location == ["gpx", "rte", "rtept"]:
                self.route.addPoint(self.rp)
                [
                    observer.nextRoutePoint(self.rp)
                    for observer in self.observers
                ]

            elif self.location == ["gpx", "rte", "rtept", "sym"]:
                self.rp.sym = content

            elif self.location == ["gpx", "rte", "rtept", "type"]:
                self.rp.type = content

        elif self.location == ['gpx']:
            pass
        elif self.location == ["gpx", "metadata", "bounds"]:
            minlat = self.attrs.getValue("minlat")
            minlon = self.attrs.getValue("minlon")
            maxlat = self.attrs.getValue("maxlat")
            maxlon = self.attrs.getValue("maxlon")

            bounds = Bounds()
            bounds.registerPoint(Point(minlon, minlat))
            bounds.registerPoint(Point(maxlon, maxlat))

            [observer.haveBounds(bounds) for observer in self.observers]

        elif self.location[:2] == ["gpx", "metadata"]:
            #         elif self.location == ["gpx", "metadata", "time"]:
            pass
        else:
            self.log.i("IGNORED: %s, content: %s" % (self.location, content))

        self.content = None

        self.location.pop()