|
| 1 | +-- Custom "hexagon" layer |
| 2 | +-- Adapted from https://github.com/CrunchyData/pg_tileserv |
| 3 | +-- |
| 4 | +-- |
| 5 | +-- Given an input ZXY tile coordinate, output a set of hexagons |
| 6 | +-- (and hexagon coordinates) in web mercator that cover that tile |
| 7 | +CREATE OR REPLACE FUNCTION tilehexagons( |
| 8 | + bounds geometry, |
| 9 | + step integer, |
| 10 | + epsg integer, |
| 11 | + OUT geom geometry(Polygon, 3857), OUT i integer, OUT j integer) |
| 12 | +RETURNS SETOF record AS $$ |
| 13 | +DECLARE |
| 14 | + maxbounds geometry := ST_TileEnvelope(0, 0, 0); |
| 15 | + edge float8; |
| 16 | +BEGIN |
| 17 | + edge := (ST_XMax(bounds) - ST_XMin(bounds)) / pow(2, step); |
| 18 | + FOR geom, i, j IN |
| 19 | + SELECT ST_SetSRID(hexagon(h.i, h.j, edge), epsg), h.i, h.j |
| 20 | + FROM hexagoncoordinates(bounds, edge) h |
| 21 | + LOOP |
| 22 | + IF maxbounds ~ geom AND bounds && geom THEN |
| 23 | + RETURN NEXT; |
| 24 | + END IF; |
| 25 | + END LOOP; |
| 26 | +END; |
| 27 | +$$ |
| 28 | +LANGUAGE 'plpgsql' |
| 29 | +IMMUTABLE |
| 30 | +STRICT |
| 31 | +PARALLEL SAFE; |
| 32 | + |
| 33 | +-- Given coordinates in the hexagon tiling that has this |
| 34 | +-- edge size, return the built-out hexagon |
| 35 | +CREATE OR REPLACE FUNCTION hexagon( |
| 36 | + i integer, |
| 37 | + j integer, |
| 38 | + edge float8 |
| 39 | +) |
| 40 | +RETURNS geometry AS $$ |
| 41 | +DECLARE |
| 42 | + h float8 := edge*cos(pi()/6.0); |
| 43 | + cx float8 := 1.5*i*edge; |
| 44 | + cy float8 := h*(2*j+abs(i%2)); |
| 45 | +BEGIN |
| 46 | +RETURN ST_MakePolygon(ST_MakeLine(ARRAY[ |
| 47 | + ST_MakePoint(cx - 1.0*edge, cy + 0), |
| 48 | + ST_MakePoint(cx - 0.5*edge, cy + -1*h), |
| 49 | + ST_MakePoint(cx + 0.5*edge, cy + -1*h), |
| 50 | + ST_MakePoint(cx + 1.0*edge, cy + 0), |
| 51 | + ST_MakePoint(cx + 0.5*edge, cy + h), |
| 52 | + ST_MakePoint(cx - 0.5*edge, cy + h), |
| 53 | + ST_MakePoint(cx - 1.0*edge, cy + 0) |
| 54 | + ])); |
| 55 | +END; |
| 56 | +$$ |
| 57 | +LANGUAGE 'plpgsql' |
| 58 | +IMMUTABLE |
| 59 | +STRICT |
| 60 | +PARALLEL SAFE; |
| 61 | + |
| 62 | +-- Given a square bounds, find all the hexagonal cells |
| 63 | +-- of a hex tiling (determined by edge size) |
| 64 | +-- that might cover that square (slightly over-determined) |
| 65 | +CREATE OR REPLACE FUNCTION hexagoncoordinates( |
| 66 | + bounds geometry, |
| 67 | + edge float8, |
| 68 | + OUT i integer, |
| 69 | + OUT j integer |
| 70 | +) |
| 71 | +RETURNS SETOF record AS $$ |
| 72 | +DECLARE |
| 73 | + h float8 := edge*cos(pi()/6); |
| 74 | + mini integer := floor(st_xmin(bounds) / (1.5*edge)); |
| 75 | + minj integer := floor(st_ymin(bounds) / (2*h)); |
| 76 | + maxi integer := ceil(st_xmax(bounds) / (1.5*edge)); |
| 77 | + maxj integer := ceil(st_ymax(bounds) / (2*h)); |
| 78 | +BEGIN |
| 79 | + FOR i, j IN |
| 80 | + SELECT a, b |
| 81 | + FROM generate_series(mini, maxi) a, |
| 82 | + generate_series(minj, maxj) b |
| 83 | + LOOP |
| 84 | + RETURN NEXT; |
| 85 | + END LOOP; |
| 86 | +END; |
| 87 | +$$ |
| 88 | +LANGUAGE 'plpgsql' |
| 89 | +IMMUTABLE |
| 90 | +STRICT |
| 91 | +PARALLEL SAFE; |
| 92 | + |
| 93 | +-- Given an input tile, generate the covering hexagons Step parameter determines |
| 94 | +-- how many hexagons to generate per tile. |
| 95 | +CREATE OR REPLACE FUNCTION hexagon( |
| 96 | + -- mandatory parameters |
| 97 | + xmin float, |
| 98 | + ymin float, |
| 99 | + xmax float, |
| 100 | + ymax float, |
| 101 | + epsg integer, |
| 102 | + -- additional parameters |
| 103 | + query_params json |
| 104 | +) |
| 105 | +RETURNS bytea AS $$ |
| 106 | +DECLARE |
| 107 | + result bytea; |
| 108 | + step integer; |
| 109 | + bounds geometry; |
| 110 | +BEGIN |
| 111 | + -- Find the bbox bounds |
| 112 | + bounds := ST_MakeEnvelope(xmin, ymin, xmax, ymax, epsg); |
| 113 | + |
| 114 | + step := coalesce((query_params ->> 'step')::int, 4); |
| 115 | + |
| 116 | + WITH |
| 117 | + rows AS ( |
| 118 | + -- All the hexes that interact with this tile |
| 119 | + SELECT h.i, h.j, h.geom |
| 120 | + FROM TileHexagons(bounds, step, epsg) h |
| 121 | + ), |
| 122 | + mvt AS ( |
| 123 | + -- Usual tile processing, ST_AsMVTGeom simplifies, quantizes, |
| 124 | + -- and clips to tile boundary |
| 125 | + SELECT ST_AsMVTGeom(rows.geom, bounds) AS geom, rows.i, rows.j |
| 126 | + FROM rows |
| 127 | + ) |
| 128 | + -- Generate MVT encoding of final input record |
| 129 | + SELECT ST_AsMVT(mvt, 'default') |
| 130 | + INTO result |
| 131 | + FROM mvt; |
| 132 | + |
| 133 | + RETURN result; |
| 134 | +END; |
| 135 | +$$ |
| 136 | +LANGUAGE 'plpgsql' |
| 137 | +STABLE |
| 138 | +STRICT |
| 139 | +PARALLEL SAFE; |
| 140 | + |
0 commit comments