Exemplo n.º 1
0
    def get_bike_trip_path(start, end, client):
        """
        Given a bike trip starting point, a bike trip ending point, and a Google Maps client, returns a list of
        coordinates corresponding with the path that that bike probably took, as reported by Google Maps.

        Parameters
        ----------
        start: list
            The starting point coordinates, in [latitude, longitude] (or [y, x]) format.
        end: list
            The end point coordinates, in [latitude, longitude] (or [y, x]) format.
        client: googlemaps.Client
            A `googlemaps.Client` instance, as returned by e.g. `import_google_credentials()`.

        Returns
        -------
        The list of [latitude, longitude] coordinates for the given bike trip.
        """
        codec = PolylineCodec()
        req = client.directions(start, end, mode='bicycling')
        polylines = [step['polyline']['points'] for step in [leg['steps'] for leg in req[0]['legs']][0]]
        coords = []
        for polyline in polylines:
            coords += codec.decode(polyline)
        return coords
Exemplo n.º 2
0
def get_json(*paths):
    all_json = []
    for path in paths:
        with open(path, 'r') as f:
            data = f.read()
        j = json.loads(data)
        pc = PC()
        for item in j:
            item['path'] = pc.decode(item['path'])
        all_json.extend(j)
    return all_json
Exemplo n.º 3
0
def get_json(*paths):
    all_json = []
    for path in paths:
        with open(path, 'r') as f:
            data = f.read()
        j = json.loads(data)
        pc = PC()
        for item in j:
            item['path'] = pc.decode(item['path'])
        all_json.extend(j)
    return all_json
class GooglePaths():
    """
    This tries to get all the coordinates along the start and end of a path for a user
    """

    def __init__(self, startLatitude, startLongitude, endLatitude, endLongitude, pickupStruct, cabRideNumber):
        self.baseUrl = "https://maps.googleapis.com/maps/api/directions/json?"
        self.startLatitude = startLatitude
        self.startLongitude = startLongitude
        self.endLatitude = endLatitude
        self.endLongitude = endLongitude
        self.pickupTime = pickupStruct
        self.cabRideNumber = cabRideNumber
        self.apiKey = "AIzaSyCuzBdk6sIIrrJpgQmcJbwtfumumuRLStU"
        self.polylineCodec = PolylineCodec()


    def __makeRequest(self):
        requestUrl = self.baseUrl + "origin= " + self.startLatitude + "," + self.startLongitude + "&destination= " + self.endLatitude + "," + self.endLongitude + "&key= " + self.apiKey
        response = requests.request("GET", requestUrl)
        return response.json()

    def convertToLines(self, responseJson):
        polyline = responseJson["routes"][0]["overview_polyline"]["points"]
        points =  self.polylineCodec.decode(polyline)
        stepsList =[]
        counter = 0
        for lat, long in points:
            counter += 1
            stepsList.append({"time": str(pickupTime + datetime.timedelta(seconds=counter+5)),
                              "cabRideNumber": self.cabRideNumber,
                              'latitude': lat,
                              'longitude': long})

        stepsListFinal = [stepsList[0]]
        length = len(stepsList)
        sample = []
        if len(stepsList)-2 > 20:
            sample = random.sample(stepsList[1: (length-2)], length/6)

        stepsListFinal = stepsListFinal + sample + [stepsList[length -1]]

        return stepsListFinal


    def getPaths(self):
        self.responseJson = self.__makeRequest()
        self.lines = self.convertToLines(self.responseJson)
        return self.lines
Exemplo n.º 5
0
    def get_rebalancing_trip_path_time_estimate_tuple(start, end, client):
        """
        Given a re-balancing trip starting point, a re-balancing trip ending point, and a Google Maps client,
        returns a list of coordinates corresponding with the path that van probably took, as reported by Google Maps,
        as well as a time estimate.

        The need to return a tuple containing not just the path (as in the case of very similar `bike_tripper`) stems
        from the fact that whereas for bikes we have a precise time in transit, we have no such information for
        rebalancing van trips, meaning that we have to calculate the time taken and timing of such trips ourselves.

        Parameters
        ----------
        start: list
            The starting point coordinates, in [latitude, longitude] (or [y, x]) format.
        end: list
            The end point coordinates, in [latitude, longitude] (or [y, x]) format.
        client: googlemaps.Client
            A `googlemaps.Client` instance, as returned by e.g. `import_google_credentials()`.

        Returns
        -------
        The list of [latitude, longitude] coordinates for the given bike trip.
        """
        codec = PolylineCodec()
        req = client.directions(start, end, mode='driving')
        # Get the time estimates.
        # Raw time estimate results are strings of the form "1 min", "5 mins", "1 hour 5 mins", "2 hours 5 mins", etc.
        time_estimates_raw = [step['duration']['text'] for step in [leg['steps'] for leg in req[0]['legs']][0]]
        time_estimate_mins = 0
        for time_estimate_raw in time_estimates_raw:
            # Can we really get an hour+ estimate biking within the city? Possibly not but I won't risk it.
            if "min" in time_estimate_raw and "hour" not in time_estimate_raw:
                time_estimate_mins += int(time_estimate_raw.split(" ")[0])
            elif "hour" in time_estimate_raw:
                time_estimate_mins += 60 * int(time_estimate_raw.split(" ")[0])
                if "min" in time_estimate_raw:
                    time_estimate_mins += int(time_estimate_raw.split(" ")[2])
                else:
                    # Uh-oh.
                    pass
        # Get the polylines.
        polylines = [step['polyline']['points'] for step in [leg['steps'] for leg in req[0]['legs']][0]]
        coords = []
        for polyline in polylines:
            coords += codec.decode(polyline)
        # Return
        return coords, time_estimate_mins
Exemplo n.º 6
0
def gpolyfiles2shp(pattern, ofname=None):
    files = glob(pattern)
    gpx = GpxRoute()
    with ThreadPoolExecutor(max_workers=4) as pool:
        futures2poly = {pool.submit(get_content, fname): fname for fname in files}
        for future in as_completed(futures2poly):
            fname = futures2poly[future]
            print('Handling %r' % fname)
            if future.exception() is not None:
                print('%r generated an exception: %s' % (fname, future.exception()))
                continue

            pcodec = PolylineCodec()

            polyxy = pcodec.decode(future.result())
            gpx_route = new_gpx_route(polyxy, name=fname)
            gpx.routes.append(gpx_route)

    with open(ofname, 'wb') as ofile:
        ofile.write(gpx.to_xml())

    print(ofname)
Exemplo n.º 7
0
def gpoly2shp(
    ifname, ofname=None,
    geom_path='routes/route/polyline-definition/polyline',
    time_path='routes/route/summary/time',
    length_path='routes/route/summary/length',
):
    response = get_content(ifname)
    pcodec = PolylineCodec()
    polyxy = pcodec.decode(get_item(response, geom_path))
    length = int(get_item(response, length_path))
    time = int(get_item(response, time_path))

    schema = {
        'geometry': 'LineString',
        'properties': {
            'seconds': 'int',
            'meters': 'int',
            'x_start': 'float',
            'y_start': 'float',
            'x_end': 'float',
            'y_end': 'float',
            'cat': 'str',
        },
    }

    fname, extension = splitext(ifname)
    ofname = ofname or join(dirname(fname), '{}.shp'.format(basename(fname)))

    with fiona.open(ofname, mode='w', driver='ESRI Shapefile', schema=schema) as lyr:
        lyr.write({
            'geometry': mapping(LineString(polyxy)),
            'properties': {
                'seconds': time,
                'meters': length,
            }
        })

    print(ofname)
Exemplo n.º 8
0
def gpoly2gpx(ifname, output_dir=None, json_path='routes/route/polyline-definition/polyline'):
    response = get_content(ifname)
    pcodec = PolylineCodec()
    item = get_item(response, json_path)
    if item is None:

        logging.error(
            'Bad input from file {ifname}'
            '`\n`--> response {response}'
            .format(**locals()))
        sys.exit(1)

    polyxy = pcodec.decode(item)
    gpx = GpxRoute()
    gpx_route = new_gpx_route(polyxy)
    gpx.routes.append(gpx_route)

    fname, extension = splitext(ifname)
    output_dir = output_dir or dirname(fname)
    ofname = join(output_dir, '{}.gpx'.format(basename(fname)))
    with open(ofname, 'wb') as ofile:
        ofile.write(gpx.to_xml())

    print(ofname)
Exemplo n.º 9
0
class PolylineCodecTestCase(unittest.TestCase):
    def setUp(self):
        self.codec = PolylineCodec()

    def test_decode_multiple_points(self):
        d = self.codec.decode('gu`wFnfys@???nKgE??gE?????oK????fE??fE')
        self.assertEqual(d, [
            (40.641, -8.654),
            (40.641, -8.654),
            (40.641, -8.656),
            (40.642, -8.656),
            (40.642, -8.655),
            (40.642, -8.655),
            (40.642, -8.655),
            (40.642, -8.653),
            (40.642, -8.653),
            (40.642, -8.653),
            (40.641, -8.653),
            (40.641, -8.654)
        ])

    def test_decode_multiple_points_precision(self):
        d = self.codec.decode('o}oolA~ieoO???~{Bo}@??o}@?????_|B????n}@??n}@', 6)
        self.assertEqual(d, [
            (40.641, -8.654),
            (40.641, -8.654),
            (40.641, -8.656),
            (40.642, -8.656),
            (40.642, -8.655),
            (40.642, -8.655),
            (40.642, -8.655),
            (40.642, -8.653),
            (40.642, -8.653),
            (40.642, -8.653),
            (40.641, -8.653),
            (40.641, -8.654)
        ])

    def test_decode_official_example(self):
        d = self.codec.decode('_p~iF~ps|U_ulLnnqC_mqNvxq`@')
        self.assertEqual(d, [
            (38.500, -120.200),
            (40.700, -120.950),
            (43.252, -126.453)
        ])

    def test_decode_official_example_precision(self):
        d = self.codec.decode('_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI', 6)
        self.assertEqual(d, [
            (38.500, -120.200),
            (40.700, -120.950),
            (43.252, -126.453)
        ])

    def test_decode_single_point(self):
        d = self.codec.decode('gu`wFf`ys@')
        self.assertEqual(d, [
            (40.641, -8.653)
        ])

    def test_decode_single_point_precision(self):
        d = self.codec.decode('o}oolAnkcoO', 6)
        self.assertEqual(d, [
            (40.641, -8.653)
        ])

    def test_encode_multiple_points(self):
        e = self.codec.encode([
            (40.641, -8.654),
            (40.641, -8.654),
            (40.641, -8.656),
            (40.642, -8.656),
            (40.642, -8.655),
            (40.642, -8.655),
            (40.642, -8.655),
            (40.642, -8.653),
            (40.642, -8.653),
            (40.642, -8.653),
            (40.641, -8.653),
            (40.641, -8.654)
        ])
        self.assertEqual(e, 'gu`wFnfys@???nKgE??gE?????oK????fE??fE')

    def test_encode_multiple_points_precision(self):
        e = self.codec.encode([
            (40.641, -8.654),
            (40.641, -8.654),
            (40.641, -8.656),
            (40.642, -8.656),
            (40.642, -8.655),
            (40.642, -8.655),
            (40.642, -8.655),
            (40.642, -8.653),
            (40.642, -8.653),
            (40.642, -8.653),
            (40.641, -8.653),
            (40.641, -8.654)
        ], 6)
        self.assertEqual(e, 'o}oolA~ieoO???~{Bo}@??o}@?????_|B????n}@??n}@')

    def test_encode_official_example(self):
        e = self.codec.encode([
            (38.500, -120.200),
            (40.700, -120.950),
            (43.252, -126.453)
        ])
        self.assertEqual(e, '_p~iF~ps|U_ulLnnqC_mqNvxq`@')

    def test_encode_official_example_precision(self):
        e = self.codec.encode([
            (38.500, -120.200),
            (40.700, -120.950),
            (43.252, -126.453)
        ], 6)
        self.assertEqual(e, '_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI')

    def test_encode_single_point(self):
        e = self.codec.encode([
            (40.641, -8.653)
        ])
        self.assertEqual(e, 'gu`wFf`ys@')

    def test_encode_single_point_precision(self):
        e = self.codec.encode([
            (40.641, -8.653)
        ], 6)
        self.assertEqual(e, 'o}oolAnkcoO')

    def test_a_variety_of_precisions(self):
        """uses a generator to create a variety of lat-lon's across the global
            and tests a range of precision settings from 4 to 8"""

        def generator():
            while True:
                coords = []
                for i in range(2, randint(4, 10)):
                    lat, lon = uniform(-180.0, 180.0), uniform(-180.0, 180.0)
                    xy = (round(lat, 5), round(lon, 5))
                    coords.append(xy)
                yield coords

        patience = 3  # seconds.
        waypoints, okays = 0, 0

        g = generator()
        start = time.time()
        while time.time() < start + patience:
            precision = randint(4, 8)
            wp = next(g)
            waypoints += len(wp)
            polyline = self.codec.encode(wp, precision)
            wp2 = self.codec.decode(polyline, precision)
            if wp == wp2:
                okays += len(wp2)
            else:
                for idx, _ in enumerate(wp):
                    dx, dy = abs(wp[idx][0] - wp2[idx][0]), abs(wp[idx][1] - wp2[idx][1])
                    if dx > 10 ** -(precision - 1) or dy > 10 ** -(precision - 1):
                        print("idx={}, dx={}, dy={}".format(idx, dx, dy))
                    else:
                        okays += 1

        assert okays == waypoints
        print("encoded and decoded {0:.2f}% correctly for {1} waypoints @ {2} wp/sec".format(
            100 * okays / float(waypoints),
            waypoints,
            round(waypoints / patience, 0)))
Exemplo n.º 10
0
class PolylineCodecTestCase(unittest.TestCase):
    def setUp(self):
        self.codec = PolylineCodec()

    def test_decode_multiple_points(self):
        d = self.codec.decode('gu`wFnfys@???nKgE??gE?????oK????fE??fE')
        self.assertEqual(d, [(40.641, -8.654), (40.641, -8.654),
                             (40.641, -8.656), (40.642, -8.656),
                             (40.642, -8.655), (40.642, -8.655),
                             (40.642, -8.655), (40.642, -8.653),
                             (40.642, -8.653), (40.642, -8.653),
                             (40.641, -8.653), (40.641, -8.654)])

    def test_decode_multiple_points_precision(self):
        d = self.codec.decode('o}oolA~ieoO???~{Bo}@??o}@?????_|B????n}@??n}@',
                              6)
        self.assertEqual(d, [(40.641, -8.654), (40.641, -8.654),
                             (40.641, -8.656), (40.642, -8.656),
                             (40.642, -8.655), (40.642, -8.655),
                             (40.642, -8.655), (40.642, -8.653),
                             (40.642, -8.653), (40.642, -8.653),
                             (40.641, -8.653), (40.641, -8.654)])

    def test_decode_official_example(self):
        d = self.codec.decode('_p~iF~ps|U_ulLnnqC_mqNvxq`@')
        self.assertEqual(d, [(38.500, -120.200), (40.700, -120.950),
                             (43.252, -126.453)])

    def test_decode_official_example_precision(self):
        d = self.codec.decode('_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI', 6)
        self.assertEqual(d, [(38.500, -120.200), (40.700, -120.950),
                             (43.252, -126.453)])

    def test_decode_single_point(self):
        d = self.codec.decode('gu`wFf`ys@')
        self.assertEqual(d, [(40.641, -8.653)])

    def test_decode_single_point_precision(self):
        d = self.codec.decode('o}oolAnkcoO', 6)
        self.assertEqual(d, [(40.641, -8.653)])

    def test_encode_multiple_points(self):
        e = self.codec.encode([(40.641, -8.654), (40.641, -8.654),
                               (40.641, -8.656), (40.642, -8.656),
                               (40.642, -8.655), (40.642, -8.655),
                               (40.642, -8.655), (40.642, -8.653),
                               (40.642, -8.653), (40.642, -8.653),
                               (40.641, -8.653), (40.641, -8.654)])
        self.assertEqual(e, 'gu`wFnfys@???nKgE??gE?????oK????fE??fE')

    def test_encode_multiple_points_precision(self):
        e = self.codec.encode([(40.641, -8.654), (40.641, -8.654),
                               (40.641, -8.656), (40.642, -8.656),
                               (40.642, -8.655), (40.642, -8.655),
                               (40.642, -8.655), (40.642, -8.653),
                               (40.642, -8.653), (40.642, -8.653),
                               (40.641, -8.653), (40.641, -8.654)], 6)
        self.assertEqual(e, 'o}oolA~ieoO???~{Bo}@??o}@?????_|B????n}@??n}@')

    def test_encode_official_example(self):
        e = self.codec.encode([(38.500, -120.200), (40.700, -120.950),
                               (43.252, -126.453)])
        self.assertEqual(e, '_p~iF~ps|U_ulLnnqC_mqNvxq`@')

    def test_encode_official_example_precision(self):
        e = self.codec.encode([(38.500, -120.200), (40.700, -120.950),
                               (43.252, -126.453)], 6)
        self.assertEqual(e, '_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI')

    def test_encode_single_point(self):
        e = self.codec.encode([(40.641, -8.653)])
        self.assertEqual(e, 'gu`wFf`ys@')

    def test_encode_single_point_precision(self):
        e = self.codec.encode([(40.641, -8.653)], 6)
        self.assertEqual(e, 'o}oolAnkcoO')

    def test_a_variety_of_precisions(self):
        """uses a generator to create a variety of lat-lon's across the global
            and tests a range of precision settings from 4 to 8"""
        def generator():
            while True:
                coords = []
                for i in range(2, randint(4, 10)):
                    lat, lon = uniform(-180.0, 180.0), uniform(-180.0, 180.0)
                    xy = (round(lat, 5), round(lon, 5))
                    coords.append(xy)
                yield coords

        patience = 3  # seconds.
        waypoints, okays = 0, 0

        g = generator()
        start = time.time()
        while time.time() < start + patience:
            precision = randint(4, 8)
            wp = next(g)
            waypoints += len(wp)
            polyline = self.codec.encode(wp, precision)
            wp2 = self.codec.decode(polyline, precision)
            if wp == wp2:
                okays += len(wp2)
            else:
                for idx, _ in enumerate(wp):
                    dx, dy = abs(wp[idx][0] - wp2[idx][0]), abs(wp[idx][1] -
                                                                wp2[idx][1])
                    if dx > 10**-(precision - 1) or dy > 10**-(precision - 1):
                        print("idx={}, dx={}, dy={}".format(idx, dx, dy))
                    else:
                        okays += 1

        assert okays == waypoints
        print(
            "encoded and decoded {0:.2f}% correctly for {1} waypoints @ {2} wp/sec"
            .format(100 * okays / float(waypoints), waypoints,
                    round(waypoints / patience, 0)))
Exemplo n.º 11
0
gmaps = googlemaps.Client(key='')

dirs = []

for i in cbike.index:

    xa = cbike['start station longitude'][i]
    ya = cbike['start station latitude'][i]
    xb = cbike['end station longitude'][i]
    yb = cbike['end station latitude'][i]
    directions = gmaps.directions((ya, xa), (yb, xb), mode='bicycling')

    #print directions
    codec = PolylineCodec()

    path = []

    for s in directions[0]['legs'][0]['steps']:

        path += codec.decode(s['polyline']['points'])
    # swap lat and lon
    pp = zip(*path)
    path = zip(pp[1], pp[0])
    if len(path) > 1:
        lines = geometry.LineString(path)
        kml = simplekml.Kml()
        ls = kml.newlinestring(name='sample')
        ls.coords = lines.coords
        kml.save("data/paths/path500_" + str(i) + ".kml")
gmaps = googlemaps.Client(key='')

dirs = []


for i in cbike.index:

    xa = cbike['start station longitude'][i]
    ya = cbike['start station latitude'][i]
    xb = cbike['end station longitude'][i]
    yb = cbike['end station latitude'][i]
    directions = gmaps.directions((ya,xa), (yb,xb), mode='bicycling')

    #print directions
    codec = PolylineCodec()

    path = []

    for s in directions[0]['legs'][0]['steps']:

        path += codec.decode(s['polyline']['points'])
    # swap lat and lon
    pp = zip(*path)
    path = zip(pp[1], pp[0])
    if len(path) > 1:
        lines = geometry.LineString(path)
        kml = simplekml.Kml()
        ls = kml.newlinestring(name='sample')
        ls.coords = lines.coords
        kml.save("data/paths/path500_" + str(i) + ".kml")
Exemplo n.º 13
0
class PolylineCodecTestCase(unittest.TestCase):
    def setUp(self):
        self.codec = PolylineCodec()

    def test_decode_multiple_points(self):
        d = self.codec.decode('gu`wFnfys@???nKgE??gE?????oK????fE??fE')
        self.assertEqual(d, [(40.641, -8.654), (40.641, -8.654),
                             (40.641, -8.656), (40.642, -8.656),
                             (40.642, -8.655), (40.642, -8.655),
                             (40.642, -8.655), (40.642, -8.653),
                             (40.642, -8.653), (40.642, -8.653),
                             (40.641, -8.653), (40.641, -8.654)])

    def test_decode_multiple_points_precision(self):
        d = self.codec.decode('o}oolA~ieoO???~{Bo}@??o}@?????_|B????n}@??n}@',
                              6)
        self.assertEqual(d, [(40.641, -8.654), (40.641, -8.654),
                             (40.641, -8.656), (40.642, -8.656),
                             (40.642, -8.655), (40.642, -8.655),
                             (40.642, -8.655), (40.642, -8.653),
                             (40.642, -8.653), (40.642, -8.653),
                             (40.641, -8.653), (40.641, -8.654)])

    def test_decode_official_example(self):
        d = self.codec.decode('_p~iF~ps|U_ulLnnqC_mqNvxq`@')
        self.assertEqual(d, [(38.500, -120.200), (40.700, -120.950),
                             (43.252, -126.453)])

    def test_decode_official_example_precision(self):
        d = self.codec.decode('_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI', 6)
        self.assertEqual(d, [(38.500, -120.200), (40.700, -120.950),
                             (43.252, -126.453)])

    def test_decode_single_point(self):
        d = self.codec.decode('gu`wFf`ys@')
        self.assertEqual(d, [(40.641, -8.653)])

    def test_decode_single_point_precision(self):
        d = self.codec.decode('o}oolAnkcoO', 6)
        self.assertEqual(d, [(40.641, -8.653)])

    def test_encode_multiple_points(self):
        e = self.codec.encode([(40.641, -8.654), (40.641, -8.654),
                               (40.641, -8.656), (40.642, -8.656),
                               (40.642, -8.655), (40.642, -8.655),
                               (40.642, -8.655), (40.642, -8.653),
                               (40.642, -8.653), (40.642, -8.653),
                               (40.641, -8.653), (40.641, -8.654)])
        self.assertEqual(e, 'gu`wFnfys@???nKgE??gE?????oK????fE??fE')

    def test_encode_multiple_points_precision(self):
        e = self.codec.encode([(40.641, -8.654), (40.641, -8.654),
                               (40.641, -8.656), (40.642, -8.656),
                               (40.642, -8.655), (40.642, -8.655),
                               (40.642, -8.655), (40.642, -8.653),
                               (40.642, -8.653), (40.642, -8.653),
                               (40.641, -8.653), (40.641, -8.654)], 6)
        self.assertEqual(e, 'o}oolA~ieoO???~{Bo}@??o}@?????_|B????n}@??n}@')

    def test_encode_official_example(self):
        e = self.codec.encode([(38.500, -120.200), (40.700, -120.950),
                               (43.252, -126.453)])
        self.assertEqual(e, '_p~iF~ps|U_ulLnnqC_mqNvxq`@')

    def test_encode_official_example_precision(self):
        e = self.codec.encode([(38.500, -120.200), (40.700, -120.950),
                               (43.252, -126.453)], 6)
        self.assertEqual(e, '_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI')

    def test_encode_single_point(self):
        e = self.codec.encode([(40.641, -8.653)])
        self.assertEqual(e, 'gu`wFf`ys@')

    def test_encode_single_point_precision(self):
        e = self.codec.encode([(40.641, -8.653)], 6)
        self.assertEqual(e, 'o}oolAnkcoO')
Exemplo n.º 14
0
class PolylineCodecTestCase(unittest.TestCase):
    def setUp(self):
        self.codec = PolylineCodec()

    def test_decode_multiple_points(self):
        d = self.codec.decode("gu`wFnfys@???nKgE??gE?????oK????fE??fE")
        self.assertEqual(
            d,
            [
                (40.641, -8.654),
                (40.641, -8.654),
                (40.641, -8.656),
                (40.642, -8.656),
                (40.642, -8.655),
                (40.642, -8.655),
                (40.642, -8.655),
                (40.642, -8.653),
                (40.642, -8.653),
                (40.642, -8.653),
                (40.641, -8.653),
                (40.641, -8.654),
            ],
        )

    def test_decode_multiple_points_precision(self):
        d = self.codec.decode("o}oolA~ieoO???~{Bo}@??o}@?????_|B????n}@??n}@", 6)
        self.assertEqual(
            d,
            [
                (40.641, -8.654),
                (40.641, -8.654),
                (40.641, -8.656),
                (40.642, -8.656),
                (40.642, -8.655),
                (40.642, -8.655),
                (40.642, -8.655),
                (40.642, -8.653),
                (40.642, -8.653),
                (40.642, -8.653),
                (40.641, -8.653),
                (40.641, -8.654),
            ],
        )

    def test_decode_official_example(self):
        d = self.codec.decode("_p~iF~ps|U_ulLnnqC_mqNvxq`@")
        self.assertEqual(d, [(38.500, -120.200), (40.700, -120.950), (43.252, -126.453)])

    def test_decode_official_example_precision(self):
        d = self.codec.decode("_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI", 6)
        self.assertEqual(d, [(38.500, -120.200), (40.700, -120.950), (43.252, -126.453)])

    def test_decode_single_point(self):
        d = self.codec.decode("gu`wFf`ys@")
        self.assertEqual(d, [(40.641, -8.653)])

    def test_decode_single_point_precision(self):
        d = self.codec.decode("o}oolAnkcoO", 6)
        self.assertEqual(d, [(40.641, -8.653)])

    def test_encode_multiple_points(self):
        e = self.codec.encode(
            [
                (40.641, -8.654),
                (40.641, -8.654),
                (40.641, -8.656),
                (40.642, -8.656),
                (40.642, -8.655),
                (40.642, -8.655),
                (40.642, -8.655),
                (40.642, -8.653),
                (40.642, -8.653),
                (40.642, -8.653),
                (40.641, -8.653),
                (40.641, -8.654),
            ]
        )
        self.assertEqual(e, "gu`wFnfys@???nKgE??gE?????oK????fE??fE")

    def test_encode_multiple_points_precision(self):
        e = self.codec.encode(
            [
                (40.641, -8.654),
                (40.641, -8.654),
                (40.641, -8.656),
                (40.642, -8.656),
                (40.642, -8.655),
                (40.642, -8.655),
                (40.642, -8.655),
                (40.642, -8.653),
                (40.642, -8.653),
                (40.642, -8.653),
                (40.641, -8.653),
                (40.641, -8.654),
            ],
            6,
        )
        self.assertEqual(e, "o}oolA~ieoO???~{Bo}@??o}@?????_|B????n}@??n}@")

    def test_encode_official_example(self):
        e = self.codec.encode([(38.500, -120.200), (40.700, -120.950), (43.252, -126.453)])
        self.assertEqual(e, "_p~iF~ps|U_ulLnnqC_mqNvxq`@")

    def test_encode_official_example_precision(self):
        e = self.codec.encode([(38.500, -120.200), (40.700, -120.950), (43.252, -126.453)], 6)
        self.assertEqual(e, "_izlhA~rlgdF_{geC~ywl@_kwzCn`{nI")

    def test_encode_single_point(self):
        e = self.codec.encode([(40.641, -8.653)])
        self.assertEqual(e, "gu`wFf`ys@")

    def test_encode_single_point_precision(self):
        e = self.codec.encode([(40.641, -8.653)], 6)
        self.assertEqual(e, "o}oolAnkcoO")