4

Looking for an implementation in PostGIS for generating a hexagonal grid that covers the the whole planet in order to aggregate data over each hexagon.

Any pointer in the right direction would be of great help!

End product: - A table containing the center point for each hexagon in a hexagonal grid that covers the whole world. - The hexagons have a fixed area

  • You can't cover a sphere (or vaguely spherical blob) in hexagons. If you use hexagons and pentagons you need 12 pentagons. – dmuir Jun 13 '18 at 14:39
  • 1
    @dmuir: To be more precise: It is not possible to regularly cover a sphere with hexagons (such that every vertex has exactly three incident hexagons). You can still produce irregular covers. Instead of the 12 pentagons, you could also use 6 quads, or 4 triangles. – Nico Schertler Jun 13 '18 at 17:50
  • Doesn't really matter if they are not perfect hexagons. I'ts fine if they are distorted, just need them to be approximately the same size for data binning. I would'nt even mind if they were overlapping a bit here and there. I'd just find the distance to the closest center. – user9936632 Jun 14 '18 at 18:08
  • 1
    Out of curiosity: Why does it need to be hexagons? If you just want to accumulate data into equiareal bins, the Voronoi diagram of a Poisson disk sampling might work as well. – Nico Schertler Jun 19 '18 at 18:42
  • @user9936632 have you found a solution? I'd be very interested on it :) – Jim Jones Jun 21 '18 at 06:38
  • @NicoSchertler I actually don't need hexagons, needed a way of evenly distributing points on the globe and use them for spatial aggregation and I solved that using: https://stackoverflow.com/questions/9600801/evenly-distributing-n-points-on-a-sphere – user9936632 Jun 25 '18 at 08:53
  • 1
    @jim-jones, thanks for the help so far. As I wrote above, I went with a different approach not using map projections etc at all. – user9936632 Jun 25 '18 at 08:54
  • @user9936632 alright. I will still leave the answer as it is (with hexagons), since it may help other users. Thanks for reporting. – Jim Jones Jun 25 '18 at 09:15

1 Answers1

5

Some time ago I adapted a function to generate hexagons that might be exactly what you're looking for. It takes the parameters cell width, and the coordinates for southwest and northeast corners, and generates a hexagonal grid.

CREATE OR REPLACE FUNCTION create_hexagons(width FLOAT, xmin FLOAT, ymin FLOAT, xmax FLOAT, ymax FLOAT)
RETURNS TABLE (_gid INTEGER, _geom GEOMETRY) AS $$
DECLARE
  b FLOAT := width/2;
  a FLOAT := b/2;
  c FLOAT := 2*a;
  height FLOAT := 2*a+c;
  ncol FLOAT := ceil(abs(xmax-xmin)/width);
  nrow FLOAT := ceil(abs(ymax-ymin)/height);
  polygon_string VARCHAR := 'POLYGON((' || 
    0 || ' ' || 0 || ' , ' || b || ' ' || a || ' , ' || b || ' ' || a+c || ' , ' || 0 || ' ' || a+c+a || ' , ' ||
   -1*b || ' ' || a+c || ' , ' || -1*b || ' ' || a || ' , ' || 0 || ' ' || 0 || '))';
BEGIN
  CREATE TEMPORARY TABLE tmp (gid serial NOT NULL PRIMARY KEY,geom GEOMETRY(POLYGON)) ON COMMIT DROP;
  INSERT INTO tmp (geom)   
  SELECT ST_Translate(geom, x_series*(2*a+c)+xmin, y_series*(2*(c+a))+ymin)
  FROM generate_series(0, ncol::INT, 1) AS x_series,
       generate_series(0, nrow::INT,1 ) AS y_series,
    (SELECT polygon_string::GEOMETRY AS geom
     UNION
     SELECT ST_Translate(polygon_string::GEOMETRY, b, a + c) AS geom) AS two_hex;
    ALTER TABLE tmp ALTER COLUMN geom TYPE GEOMETRY(POLYGON, 4326) USING ST_SetSRID(geom, 4326);   
    RETURN QUERY (SELECT gid, geom FROM tmp);    
END;
$$ LANGUAGE plpgsql;

This function returns a table with the columns _gid and _geom, containing an identifier and the geometry for each hexagon, respectively.

CREATE TABLE t AS
SELECT * FROM create_hexagons(1.0, -180, -90, 180, 45) 

With these parameters, the function generates a grid with 98192 hexagons covering the whole world:

Hexagons covering the whole world

Here a bit closer, so that you can see the grid:

Hexagons covering the whole world - Europe Overview

If you're only interested in covering land, you can create a subset of these hexagons based on a geometry of your choice using ST_Intersects:

CREATE TABLE t_overlap AS 
SELECT t._gid,t._geom FROM t,world 
WHERE ST_Intersects(world.geom,t._geom)

This query will create a subset with a grid containing 35911 hexagons, which intersect with the geometries from the world map:

enter image description here

The world map used in this answer can be downloaded as shapefile here.

End product: - A table containing the center point for each hexagon in a hexagonal grid that covers the whole world. - The hexagons have a fixed area

Generating the centroids for each hexagon isn't a big deal either (see ST_Centroid):

CREATE TABLE t_overlap_centroid AS
SELECT ST_Centroid(_geom) FROM t_overlap;

enter image description here

Jim Jones
  • 18,404
  • 3
  • 35
  • 44
  • Thanks for this! A little problem though. One little problem though. The areas are differing quite a lot. They need to be approximately the same size. – user9936632 Jun 14 '18 at 18:11
  • 1
    @user9936632 I see. Can you tell me how did you find it out? At least with `SELECT DISTINCT ST_Area(_geom) FROM t_overlap;` I can see that all elements of the grid have **exactly** the same area, namely `0.75`. – Jim Jones Jun 15 '18 at 06:42
  • SELECT distinct ST_Area(_geom::geography, FALSE) FROM t – user9936632 Jun 15 '18 at 09:51
  • Need them to be approximately the same geographical area. – user9936632 Jun 15 '18 at 09:52
  • I see, over a spheroid they'll indeed differ a bit in sqm. – Jim Jones Jun 15 '18 at 09:54
  • @JimJones Is there a to draw a circle (or perhaps a polygon) covering only a certain city, supposing I know the coordinates points places in the city centre? That way, I can filter may filter the points with such city to consider for analysis. – arilwan Jun 17 '20 at 18:27
  • @arilwan you mean something like this? https://stackoverflow.com/a/49985343/2275388 – Jim Jones Jun 17 '20 at 18:39
  • @JimJones I'm sorry to check on with this again, please. I am looking for a way to draw a rectangular area around the city of Beijing `lat: 39.45 - 40.05` and `lon: 115.4166 - 117.5`. As my research intends to cover the municipality (as defined by these coordinates). – arilwan Jul 06 '20 at 17:52
  • @arilwan have you tried using ST_MakeEnvelope? ? e.g.`SELECT ST_MakeEnvelope(115.4166, 39.45, 117.5,40.05,4326)` This query returns a polygon over Beijing and also over other cities, e.g. Langfang. Is it what you want? – Jim Jones Jul 07 '20 at 06:12