Here is a set returning function ST_CreateFishnet
that creates a 2D grid of polygon geometries:
CREATE OR REPLACE FUNCTION ST_CreateFishnet(
nrow integer, ncol integer,
xsize float8, ysize float8,
x0 float8 DEFAULT 0, y0 float8 DEFAULT 0,
OUT "row" integer, OUT col integer,
OUT geom geometry)
RETURNS SETOF record AS
$$
SELECT i + 1 AS row, j + 1 AS col, ST_Translate(cell, j * $3 + $5, i * $4 + $6) AS geom
FROM generate_series(0, $1 - 1) AS i,
generate_series(0, $2 - 1) AS j,
(
SELECT ('POLYGON((0 0, 0 '||$4||', '||$3||' '||$4||', '||$3||' 0,0 0))')::geometry AS cell
) AS foo;
$$ LANGUAGE sql IMMUTABLE STRICT;
where nrow
and ncol
are the number of rows and columns, xsize
and ysize
are the lengths of the cell size, and optional x0
and y0
are coordinates for the bottom-left corner.
The result is row
and col
numbers, starting from 1 at the bottom-left corner, and geom
rectangular polygons for each cell. So for example:
SELECT *
FROM ST_CreateFishnet(4, 6, 10, 10) AS cells;
row | col | geom
-----+-----+--------------------------------
1 | 1 | 0103000000010000000500000000...
2 | 1 | 0103000000010000000500000000...
3 | 1 | 0103000000010000000500000000...
4 | 1 | 0103000000010000000500000000...
1 | 2 | 0103000000010000000500000000...
2 | 2 | 0103000000010000000500000000...
...
3 | 6 | 0103000000010000000500000000...
4 | 6 | 0103000000010000000500000000...
(24 rows)
Or to make a single geometry collection for the full grid:
SELECT ST_Collect(cells.geom)
FROM ST_CreateFishnet(4, 6, 10, 10) AS cells;
![4x6 grid](https://i.stack.imgur.com/vQY0Z.png)
You can add the x0
/ y0
origin offsets (these defaulted to zero).
As for your first problem, what would you want to happen with a point that falls exactly on the boundary between two grid cells?
As for the area calculation:
geodata=# SELECT ST_Area(ST_GeogFromText('POLYGON((-10 0,-10 5,-5 5,-5
0,-10 0))'));
st_area
308911036269.806 (1 row)
geodata=# select postgis_full_version();
postgis_full_version
-------------------------------------------------------------------------------------------------------------------------------------------
POSTGIS="1.5.3" GEOS="3.3.2-CAPI-1.7.2" PROJ="Rel. 4.7.1, 23 September 2009" LIBXML="2.7.6" USE_STATS (procs from 1.5 r5385 need upgrade)
(1 row)
Seems to work for me. What version are you using??
Searching in Google I found this maillist exchange regarding AT_Area and polys touching the equator:
postgis-users
Perhaps, to better understand, could you run the same query but crossing the equator. Such as:
geodata=# SELECT ST_Area(ST_GeographyFromText('POLYGON((-2 -1,-2 1,-1 1,-1 -1,-2 -1))'));
st_area
------------------
24728063597.0354
(1 row)
Best Answer
There are two approaches: vector-based and raster-based. The former is more accurate, and the later is generally faster but much more complicated.
Vector approach
Use the fraction of area overlap between each polygon and grid cell to weight the values. Say you have a
grid
(e.g. created this way), and an irregularpoly
with values inval
. To determineval
ongrid
:This query will take a while there are many tens of thousands of
grid
cells (i.e. 100 × 100, or more).Raster approach
The idea behind this method is if you rasterise values from an irregular polygon, you get the values. However, the rasterisation methods are limited, as they don't consider the area fraction of polygons within each pixel. So you will need to modify this approach by rasterising at a finer resolution than desired, then generalise the refined raster to the desired resolution. The accuracy of this method increases with a finer initial raster resolution.
I can add more details to this method, but it is much more complicated. I did this approach first with gdal_rasterize with files in a shell, then I moved the logic to GDAL/Python using gdal.RasterizeLayer.