Пример #1
0
def visualize_hexagons(hexagons, color="red", folium_map=None):
    """
    hexagons is a list of hexcluster. Each hexcluster is a list of hexagons. 
    eg. [[hex1, hex2], [hex3, hex4]]
    """
    polylines = []
    lat = []
    lng = []
    for hex in hexagons:
        polygons = h3.h3_set_to_multi_polygon([hex], geo_json=False)
        # flatten polygons into loops.
        outlines = [loop for polygon in polygons for loop in polygon]
        polyline = [outline + [outline[0]] for outline in outlines][0]
        lat.extend(map(lambda v: v[0], polyline))
        lng.extend(map(lambda v: v[1], polyline))
        polylines.append(polyline)

    if folium_map is None:
        m = folium.Map(location=[sum(lat) / len(lat),
                                 sum(lng) / len(lng)],
                       zoom_start=13,
                       tiles='cartodbpositron')
    else:
        m = folium_map
    for polyline in polylines:
        my_PolyLine = folium.PolyLine(locations=polyline,
                                      weight=8,
                                      color=color)
        m.add_child(my_PolyLine)
    return m
Пример #2
0
    def test_h3_set_to_multi_polygon_single_geo_json(self):
        h3_addresses = ['89283082837ffff']
        multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses, True)
        vertices = h3.h3_to_geo_boundary(h3_addresses[0], True)

        # We shift the expected circular list so that it starts from multi_polygon[0][0][0],
        # since output starting from any vertex would be correct as long as it's in order.
        expected_coords = self.shift_circular_list(multi_polygon[0][0][0], [
            vertices[2], vertices[3], vertices[4], vertices[5], vertices[0],
            vertices[1]
        ])
        expected = [[expected_coords]]

        self.assertEqual(len(multi_polygon), 1,
                         'polygon count matches expected')
        self.assertEqual(len(multi_polygon[0]), 1,
                         'loop count matches expected')
        self.assertEqual(
            len(multi_polygon[0][0]), 7,
            'coord count 7 matches expected according to geojson format')
        self.assertEqual(
            multi_polygon[0], multi_polygon[-1],
            'first coord should be the same as last coord according to geojson format'
        )
        self.assertAlmostEqual(
            multi_polygon[0][0][0][0], -122.42778275313199, None,
            'the coord should be (lng, lat) according to geojson format (1)')
        self.assertAlmostEqual(
            multi_polygon[0][0][0][1], 37.77598951883773, None,
            'the coord should be (lng, lat) according to geojson format (2)')
        # Discard last coord for testing below, since last coord is the same as the first one
        multi_polygon[0][0].pop()

        self.assertEqual(multi_polygon, expected, 'outline matches expected')
Пример #3
0
    def test_h3_set_to_multi_polygon_contiguous(self):
        # the second hexagon shares v0 and v1 with the first
        h3_addresses = ['89283082837ffff', '89283082833ffff']

        multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses)
        vertices0 = h3.h3_to_geo_boundary(h3_addresses[0])
        vertices1 = h3.h3_to_geo_boundary(h3_addresses[1])

        # We shift the expected circular list so that it starts from multi_polygon[0][0][0],
        # since output starting from any vertex would be correct as long as it's in order.
        expected_coords = self.shift_circular_list(multi_polygon[0][0][0], [
            vertices1[0],
            vertices1[1],
            vertices1[2],
            vertices0[1],
            vertices0[2],
            vertices0[3],
            vertices0[4],
            vertices0[5],
            vertices1[4],
            vertices1[5],
        ])
        expected = [[expected_coords]]

        self.assertEqual(len(multi_polygon), 1,
                         'polygon count matches expected')
        self.assertEqual(len(multi_polygon[0]), 1,
                         'loop count matches expected')
        self.assertEqual(len(multi_polygon[0][0]), 10,
                         'coord count 10 matches expected')

        self.assertTrue(multi_polygon == expected, 'outline matches expected')
Пример #4
0
    def test_h3_set_to_multi_polygon_2k_ring(self):
        # 2-ring in order returned by algo
        h3_addresses = h3.k_ring('8930062838bffff', 2)
        multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses)

        self.assertEqual(
            len(multi_polygon), 1, 'polygon count matches expected'
        )
        self.assertEqual(
            len(multi_polygon[0]), 1, 'loop count matches expected'
        )
        self.assertEqual(
            len(multi_polygon[0][0]), 6 * (2 * 2 + 1),
            'coord count matches expected'
        )

        # Same k-ring in random order
        h3_addresses = [
            '89300628393ffff', '89300628383ffff', '89300628397ffff',
            '89300628067ffff', '89300628387ffff', '893006283bbffff',
            '89300628313ffff', '893006283cfffff', '89300628303ffff',
            '89300628317ffff', '8930062839bffff', '8930062838bffff',
            '8930062806fffff', '8930062838fffff', '893006283d3ffff',
            '893006283c3ffff', '8930062831bffff', '893006283d7ffff',
            '893006283c7ffff'
        ]

        multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses)

        self.assertEqual(
            len(multi_polygon), 1, 'polygon count matches expected'
        )
        self.assertEqual(
            len(multi_polygon[0]), 1, 'loop count matches expected'
        )
        self.assertEqual(
            len(multi_polygon[0][0]), 6 * (2 * 2 + 1),
            'coord count matches expected'
        )

        h3_addresses = list(h3.k_ring('8930062838bffff', 6))
        h3_addresses.sort()
        multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses)

        self.assertEqual(
            len(multi_polygon[0]), 1, 'loop count matches expected'
        )
Пример #5
0
def get_spatial_features_hex(df, resolution=6, use_cache=True):

    print('Now creating spatial features')
    cache_path = os.path.join(CACHE_DIR, f'hex_spatial_df.msgpack')
    if os.path.exists(cache_path) and use_cache:
        print(f'{cache_path} exists')
        temp = pd.read_msgpack(cache_path)
    else:
        minlat = min(df.pickup_latitude)
        minlong = min(df.pickup_longitude)
        maxlat = max(df.pickup_latitude)
        maxlong = max(df.pickup_longitude)
        geoJson = {
            'type':
            'Polygon',
            'coordinates': [[[minlat, minlong], [minlat, maxlong],
                             [maxlat, maxlong], [maxlat, minlong]]]
        }

        hexagons = list(h3.polyfill(geoJson, resolution))

        xy_pickup = utm.from_latlon(df.pickup_latitude.values,
                                    df.pickup_longitude.values)
        x_pickup = list(xy_pickup[0])
        y_pickup = list(xy_pickup[1])
        pickup_point = list(zip(x_pickup, y_pickup))

        poly_hex = dict()
        for i, hex in enumerate(hexagons):
            polygons = h3.h3_set_to_multi_polygon([hex], geo_json=False)
            a = np.array(polygons[0][0])
            b = utm.from_latlon(a[:, 0], a[:, 1])
            poly_hex[i] = list(zip(b[0], b[1]))

        pick_zone = np.zeros(len(df)) - 1
        for j, p in enumerate(pickup_point):
            point = Point(p)
            for i in range(len(poly_hex)):
                polygon = Polygon(poly_hex[i])
                if polygon.contains(point):
                    pick_zone[j] = int(i)
                    break

        df['pickup_zone'] = pick_zone

        grouped_tmp = df[[
            'driver_id', 'pickup_zone', 'pickup_latitude'
        ]].groupby(['driver_id', 'pickup_zone']).count() / df[[
            'driver_id', 'pickup_zone', 'pickup_latitude'
        ]].groupby(['driver_id'])[['pickup_latitude']].count()

        temp = grouped_tmp.unstack(level=0).T
        temp.fillna(0, inplace=True)
        temp.reset_index(inplace=True)
        temp.drop(columns=['level_0'], inplace=True)
        pd.to_msgpack(cache_path, temp)
        print(f'Dumping to {cache_path}')

    return temp
Пример #6
0
    def test_h3_set_to_multi_polygon_single_geo_json(self):
        h3_addresses = ['89283082837ffff']
        multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses, True)
        vertices = h3.h3_to_geo_boundary(h3_addresses[0], True)
        # As above, could require an order update later on
        expected = [[[
            vertices[2], vertices[3], vertices[4], vertices[5], vertices[0],
            vertices[1], vertices[2]
        ]]]

        self.assertEqual(multi_polygon, expected, 'outline matches expected')
Пример #7
0
    def test_h3_set_to_multi_polygon_single(self):
        h3_addresses = ['89283082837ffff']
        multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses)
        vertices = h3.h3_to_geo_boundary(h3_addresses[0])
        # This is tricky, because output in an order starting from any vertex
        # would also be correct, but that's difficult to assert and there's
        # value in being specific here
        expected = [[[
            vertices[2], vertices[3], vertices[4], vertices[5], vertices[0],
            vertices[1]
        ]]]

        self.assertEqual(multi_polygon, expected, 'outline matches expected')
Пример #8
0
    def test_h3_set_to_multi_polygon_non_contiguous(self):
        # the second hexagon does not touch the first
        h3_addresses = ['89283082837ffff', '8928308280fffff']
        multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses)

        self.assertEqual(len(multi_polygon), 2,
                         'polygon count matches expected')
        self.assertEqual(len(multi_polygon[0]), 1,
                         'loop count matches expected')
        self.assertEqual(len(multi_polygon[0][0]), 6,
                         'coord count 1 matches expected')
        self.assertEqual(len(multi_polygon[1][0]), 6,
                         'coord count 2 matches expected')
Пример #9
0
 def test_h3_set_to_multi_polygon_non_contiguous(self):
     # the second hexagon does not touch the first
     h3_addresses = ['89283082837ffff', '8928308280fffff']
     multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses)
     # TODO: Update to appropriate expectations when the algorithm correctly
     # returns two polygons
     self.assertEqual(len(multi_polygon), 1,
                      'polygon count matches expected')
     self.assertEqual(len(multi_polygon[0]), 2,
                      'loop count matches expected')
     self.assertEqual(len(multi_polygon[0][0]), 6,
                      'coord count 1 matches expected')
     self.assertEqual(len(multi_polygon[0][1]), 6,
                      'coord count 2 matches expected')
Пример #10
0
    def test_h3_set_to_multi_polygon_single(self):
        h3_addresses = ['89283082837ffff']
        multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses)
        vertices = h3.h3_to_geo_boundary(h3_addresses[0])

        # We shift the expected circular list so that it starts from multi_polygon[0][0][0],
        # since output starting from any vertex would be correct as long as it's in order.
        expected_coords = self.shift_circular_list(multi_polygon[0][0][0], [
            vertices[2], vertices[3], vertices[4], vertices[5], vertices[0],
            vertices[1]
        ])
        expected = [[expected_coords]]

        self.assertEqual(multi_polygon, expected, 'outline matches expected')
Пример #11
0
    def test_h3_set_to_multi_polygon_hole(self):
        # Six hexagons in a ring around a hole
        h3_addresses = [
            '892830828c7ffff', '892830828d7ffff', '8928308289bffff',
            '89283082813ffff', '8928308288fffff', '89283082883ffff'
        ]
        multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses)

        self.assertEqual(len(multi_polygon), 1,
                         'polygon count matches expected')
        self.assertEqual(len(multi_polygon[0]), 2,
                         'loop count matches expected')
        self.assertEqual(len(multi_polygon[0][0]), 6 * 3,
                         'outer coord count matches expected')
        self.assertEqual(len(multi_polygon[0][1]), 6,
                         'inner coord count matches expected')
Пример #12
0
    def test_h3_set_to_multi_polygon_contiguous(self):
        # the second hexagon shares v0 and v1 with the first
        h3_addresses = ['89283082837ffff', '89283082833ffff']
        multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses)
        vertices0 = h3.h3_to_geo_boundary(h3_addresses[0])
        vertices1 = h3.h3_to_geo_boundary(h3_addresses[1])
        # As above: This test is brittle but worthwhile; it's possible we'll
        # need to update to a new start vertex if the algo is changed
        expected = [[[
            vertices1[0],
            vertices1[1],
            vertices1[2],
            vertices0[1],
            vertices0[2],
            vertices0[3],
            vertices0[4],
            vertices0[5],
            vertices1[4],
            vertices1[5],
        ]]]

        self.assertEqual(multi_polygon, expected, 'outline matches expected')
Пример #13
0
        def map_interacted(event):
            if event["type"] == "change" and event["name"] == "bounds":
                self.ht.value = f"{self.tile_id}, Map zoom: {int(a_map.zoom)}"
                self.slider.max = int(a_map.zoom) + self._max_zoom_delta
                m = event["owner"]

                b_poly = list(m.bounds_polygon)
                b_poly += [tuple(b_poly[0])]
                # m += Polyline(locations=b_poly)
                poly = geojson.Polygon(coordinates=[[(p[0], p[1])
                                                     for p in b_poly]])
                hexagons = list(h3.polyfill(dict(poly), self.slider.value))
                fc = geojson.FeatureCollection(features=[
                    geojson.Polygon(coordinates=h3.h3_set_to_multi_polygon(
                        [h], geo_json=True)[0],
                                    id=h) for h in hexagons
                ])
                self.gj.data = fc

                # Ipyleaflet buglet(?): This name is updated in the GeoJSON layer,
                # but not in the LayersControl!
                self.gj.name = f"H3"  # level {self.level}"

                self.gj.on_hover(hover)
Пример #14
0
def create_hexgrid(polygon, hex_res, geometry_col='geometry', buffer=0.000):
    """
	Takes in a geopandas geodataframe, the desired resolution, the specified geometry column and some map parameters to create a hexagon grid (and potentially plot the hexgrid

	Arguments:
		polygon {geopandas.geoDataFrame} -- geoDataFrame to be used
		hex_res {int} -- Resolution to use

	Keyword Arguments:
		geometry_col {str} -- column in the geoDataFrame that contains the geometry (default: {'geometry'})
		buffer {float} -- buffer to be used (default: {0.000})

	Returns:
		geopandas.geoDataFrame -- geoDataFrame with the hexbins and the hex_id_{resolution} column
	"""
    centroid = list(polygon.centroid.values[0].coords)[0]

    # Explode multipolygon into individual polygons
    exploded = polygon.explode().reset_index(drop=True)

    # Master lists for geodataframe
    hexagon_polygon_list = []
    hexagon_geohash_list = []

    # For each exploded polygon
    for poly in exploded[geometry_col].values:

        # Reverse coords for original polygon
        reversed_coords = [[i[1], i[0]] for i in list(poly.exterior.coords)]

        # Reverse coords for buffered polygon
        buffer_poly = poly.buffer(buffer)
        reversed_buffer_coords = [[i[1], i[0]]
                                  for i in list(buffer_poly.exterior.coords)]

        # Format input to the way H3 expects it
        aoi_input = {
            'type': 'Polygon',
            'coordinates': [reversed_buffer_coords]
        }

        # Generate list geohashes filling the AOI
        geohashes = list(h3.polyfill(aoi_input, hex_res))
        for geohash in geohashes:
            polygons = h3.h3_set_to_multi_polygon([geohash], geo_json=True)
            outlines = [loop for polygon in polygons for loop in polygon]
            polyline_geojson = [
                outline + [outline[0]] for outline in outlines
            ][0]
            hexagon_polygon_list.append(
                shapely.geometry.Polygon(polyline_geojson))
            hexagon_geohash_list.append(geohash)

    # Create a geodataframe containing the hexagon geometries and hashes
    hexgrid_gdf = gpd.GeoDataFrame()
    hexgrid_gdf['geometry'] = hexagon_polygon_list
    id_col_name = 'hex_id_' + str(hex_res)
    hexgrid_gdf[id_col_name] = hexagon_geohash_list
    hexgrid_gdf.crs = {'init': 'epsg:4326'}

    # Drop duplicate geometries
    geoms_wkb = hexgrid_gdf["geometry"].apply(lambda geom: geom.wkb)
    hexgrid_gdf = hexgrid_gdf.loc[geoms_wkb.drop_duplicates().index]

    return hexgrid_gdf
Пример #15
0
def create_hexgrid(polygon,
                   hex_res,
                   geometry_col='geometry',
                   map_zoom=12,
                   buffer=0.000,
                   stroke_weight=0.5,
                   stroke_color='blue',
                   plot=True):
    """ Takes in a geopandas geodataframe, the desired resolution, the specified geometry column
        and some map parameters to create a hexagon grid (and potentially plot the hexgrid
    """
    centroid = list(polygon.centroid.values[0].coords)[0]
    fol_map = folium.Map(location=[centroid[1], centroid[0]],
                         zoom_start=map_zoom,
                         tiles='cartodbpositron')

    # Explode multipolygon into individual polygons
    exploded = polygon.explode().reset_index(drop=True)

    # Master lists for geodataframe
    hexagon_polygon_list = []
    hexagon_geohash_list = []

    # For each exploded polygon
    for poly in exploded[geometry_col].values:

        # Reverse coords for original polygon
        coords = list(poly.exterior.coords)
        reversed_coords = []
        for i in coords:
            reversed_coords.append([i[1], i[0]])

        # Reverse coords for buffered polygon
        buffer_poly = poly.buffer(buffer)
        buffer_coords = list(buffer_poly.exterior.coords)
        reversed_buffer_coords = []
        for i in buffer_coords:
            reversed_buffer_coords.append([i[1], i[0]])

        # Format input to the way H3 expects it
        aoi_input = {
            'type': 'Polygon',
            'coordinates': [reversed_buffer_coords]
        }

        # Add polygon outline to map
        outline = reversed_coords
        outline.append(outline[0])
        outline = folium.PolyLine(locations=outline, weight=1, color='black')
        fol_map.add_child(outline)

        # Generate list geohashes filling the AOI
        geohashes = list(h3.polyfill(aoi_input, hex_res))

        # Generate hexagon polylines for Folium
        polylines = []
        lat = []
        lng = []
        for geohash in geohashes:
            polygons = h3.h3_set_to_multi_polygon([geohash], geo_json=False)
            outlines = [loop for polygon in polygons for loop in polygon]
            polyline = [outline + [outline[0]] for outline in outlines][0]
            lat.extend(map(lambda x: x[0], polyline))
            lng.extend(map(lambda x: x[1], polyline))
            polylines.append(polyline)
            hexagon_geohash_list.append(geohash)

        # Add the hexagon polylines to Folium map
        for polyline in polylines:
            my_polyline = folium.PolyLine(locations=polyline,
                                          weight=stroke_weight,
                                          color=stroke_color)
            fol_map.add_child(my_polyline)

        # Generate hexagon polygons for Shapely
        for geohash in geohashes:
            polygons = h3.h3_set_to_multi_polygon([geohash], geo_json=True)
            outlines = [loop for polygon in polygons for loop in polygon]
            polyline_geojson = [
                outline + [outline[0]] for outline in outlines
            ][0]
            hexagon_polygon_list.append(
                shapely.geometry.Polygon(polyline_geojson))

    if plot:
        display(fol_map)

    # Create a geodataframe containing the hexagon geometries and hashes
    hexgrid_gdf = gpd.GeoDataFrame()
    hexgrid_gdf['geometry'] = hexagon_polygon_list
    id_col_name = 'hex_id_' + str(hex_res)
    hexgrid_gdf[id_col_name] = hexagon_geohash_list
    hexgrid_gdf.crs = {'init': 'epsg:4326'}

    # Drop duplicate geometries
    geoms_wkb = hexgrid_gdf["geometry"].apply(lambda geom: geom.wkb)
    hexgrid_gdf = hexgrid_gdf.loc[geoms_wkb.drop_duplicates().index]

    return hexgrid_gdf
Пример #16
0
# geoJson = {'type': 'Polygon',
#  'coordinates': [[[37.813318999983238, -122.4089866999972145], [ 37.7866302000007224, -122.3805436999997056 ], [37.7198061999978478, -122.3544736999993603], [ 37.7076131999975672, -122.5123436999983966 ], [37.7835871999971715, -122.5247187000021967],  [37.8151571999998453, -122.4798767000009008]]] }

polyline = geoJson['coordinates'][0]
polyline.append(polyline[0])

lat = [p[0] for p in polyline]
lng = [p[1] for p in polyline]

m = folium.Map(location=[sum(lat) / len(lat), sum(lng) / len(lng)], zoom_start=13, tiles='cartodbpositron')
my_PolyLine = folium.PolyLine(locations=polyline, weight=8, color="blue")
m.add_child(my_PolyLine)

hexagons = list(h3.polyfill(geoJson, 7))
polylines = []
lat = []
lng = []
for hex in hexagons:
    polygons = h3.h3_set_to_multi_polygon([hex], geo_json=False)
    # flatten polygons into loops.
    outlines = [loop for polygon in polygons for loop in polygon]
    polyline = [outline + [outline[0]] for outline in outlines][0]
    lat.extend(map(lambda v: v[0], polyline))
    lng.extend(map(lambda v: v[1], polyline))
    polylines.append(polyline)

for polyline in polylines:
    my_PolyLine = folium.PolyLine(locations=polyline, weight=8, color='red')
    m.add_child(my_PolyLine)
display(m)
Пример #17
0
    def test_h3_set_to_multi_polygon_empty(self):
        h3_addresses = []
        multi_polygon = h3.h3_set_to_multi_polygon(h3_addresses)

        self.assertEqual(multi_polygon, [],
                         'no hexagons yields an empty array')