How to divide world into cells (grid) - sql

How to divide the world into cells of almost equal size, such that each lat,lon can be mapped to a different cell?
I am pretty sure I've seen a library do this, labelling cells as S1, S2, etc..
Say we have 62.356279,-99.422395 , how to map it to a 2km*2km cell named "FR,23" ?
Thank you!

PostGIS 3.1+
PostGIS 3.1 introduces very easy to use grid generators, namely ST_SquareGrid and ST_HexagonGrid. An easy way to use these functions with data from a table is to use LATERAL to execute it, e.g. cells with 0.1° in size::
Sample Data
Consider the following polygon:
Creating a squared grid with cell size of 0.1° over a given geometry
SELECT grid.* FROM isle_of_man imn,
LATERAL ST_SquareGrid(0.1,imn.geom) grid;
If you only want the cells that interersect with the geometry, just call the function ST_Intersects in the WHERE clause:
SELECT grid.* FROM isle_of_man imn,
LATERAL ST_SquareGrid(0.1,imn.geom) grid
WHERE ST_Intersects(imn.geom,grid.geom);
The same principle applies to ST_HexagonGrid:
SELECT grid.* FROM isle_of_man imn,
LATERAL ST_HexagonGrid(0.1,imn.geom) grid;
SELECT grid.* FROM isle_of_man imn,
LATERAL ST_HexagonGrid(0.1,imn.geom) grid
WHERE ST_Intersects(imn.geom,grid.geom);
Older PostGIS versions
Inspired by this post I started writing a function to do just that - it still needs some tweaking but it'll certainly give you a direction look at. The following function creates a grid with cells of a given size covering the area of a given geometry:
CREATE OR REPLACE FUNCTION public.generate_grid(_size numeric,_geom geometry)
RETURNS TABLE(gid bigint, cell geometry) LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
_bbox box2d := ST_Extent(_geom);
_ncol int := ceil(abs(ST_Xmax(_bbox)-ST_Xmin(_bbox))/_size);
_nrow int := ceil(abs(ST_Ymax(_bbox)-ST_Ymin(_bbox))/_size);
_srid int DEFAULT 4326;
BEGIN
IF ST_SRID(_geom) <> 0 THEN
IF EXISTS (SELECT 1 FROM spatial_ref_sys crs
WHERE crs.srid = ST_SRID(_geom) AND NOT crs.proj4text LIKE '+proj=longlat%') THEN
RAISE EXCEPTION 'Only lon/lat spatial reference systems are supported in this function.';
ELSE
_srid := ST_SRID($2);
END IF;
END IF;
RETURN QUERY
SELECT ROW_NUMBER() OVER (), geom FROM (
SELECT
ST_SetSRID(
(ST_PixelAsPolygons(
ST_AddBand(
ST_MakeEmptyRaster(_ncol, _nrow, ST_XMin(_bbox), ST_YMax(_bbox),_size),
'1BB'::text, 1, 0),
1, false)).geom,_srid)) j(geom);
END;
$BODY$;
Note: This function relies on the extension PostGIS Raster.
SELECT cell FROM isle_of_man,
LATERAL generate_grid(0.1,geom);
... if you're only interested in cells that overlap your polygon, add a ST_Intersects to the query:
SELECT cell FROM isle_of_man,
LATERAL generate_grid(0.1,geom)
WHERE ST_Intersects(geom,cell)
Other alternatives
Mike's fishnet function does basically the same, but you'd need to manually provide the number of rows and columns, and the coordinate pair of the lower left corner:
SELECT ST_SetSRID(cells.geom,4326)
FROM ST_CreateFishnet(4, 6, 0.1, 0.1,-4.8411, 54.0364) AS cells;
You could use this makegrid_2d function to create a grid over an area using a polygon, e.g. a grid with cells of 5000 meters in size:
CREATE TABLE grid_isle_of_man AS
SELECT 'S'||ROW_NUMBER() OVER () AS grid_id, (g).geom
FROM (
SELECT ST_Dump(makegrid_2d(geom,5000))
FROM isle_of_man) j(g)
JOIN isle_of_man ON ST_Intersects((g).geom,geom);
The same logic applies for this hexagrid function. It creates a hexagon grid with fixed sized cells over a given BBOX. You can either manually provide the BBOX (function's second parameter) or extract it from a given polygon. For instance, to create a hexagrid that matches the polygon's extent and store it in a new table with the label you want - with cells of 0.1° in size:
CREATE TABLE hexgrid_isle_of_man AS
WITH j (hex_rec) AS (
SELECT generate_hexagons(0.1,ST_Extent(geom))
FROM isle_of_man
)
SELECT 'S'||ROW_NUMBER() OVER () AS grid_id,(hex_rec).hexagon FROM j
JOIN isle_of_man t ON ST_Intersects(t.geom,(hex_rec).hexagon);
Further reading:
Import World Shapefile
Waiting for PostGIS 3.1: Grid Generators, by Paul Ramsey

Jim's answer is excellent. There are use cases though, where you don't need the actual geometries. Where, as you've mentioned, all you need is coordinates in the same cell mapping to the same code. So instead of a costly point-in-polygon operation that takes O(n) for n polygons - without index, that is ;) - you call a function that simply evaluates a formula transforming a coordinate into a code in O(1). Very handy to aggregate data spatially fast.
Personally, I love Uber's H3 library for that sort of thing, but I'm sure S2 does something similar and does it well. There is a well-maintained PostgreSQL binding for H3 and a simple aggregation example would look something like this:
SELECT h3_geo_to_h3(geom_4326, 9) AS h3res09, SUM(pop_19) AS pop_19
FROM uk_postcode_population
GROUP BY 1;
Read: Sum up the postcode-level population of the UK for each resolution 9 hexagon.
You can still create the actual hexagon geometries when you need them (whole hex grids even). But in my experience, once you commit to the grid approach, you will only need polygons for visualisation in the very end.
I should note that you can't divide the world yourself using this library - Uber has already divided it for you. So if 2km squares are a hard requirement, this is not for you.
Installing the H3 extension is not as straightforward as CREATE EXTENSION postgis, but you don't need to be a command line warrior either. You will at the least have to install PGXN, most likely PostgreSQL's extension build library and the extension itself.
Someone asked for a point-in-polygon example in the comments. This is not exactly related to the question, but highlights how one would use H3.
Polygon layer prep:
CREATE TABLE poly_h3 AS (
SELECT id, h3_polyfill(geom_4326, 13) AS h3res13
FROM poly
);
CREATE INDEX ON poly_h3 (h3res13);
Point layer prep:
ALTER TABLE points ADD COLUMN h3res13 h3index;
UPDATE points
SET h3res13 = h3_geo_to_h3(geom_p_4326, 13);
CREATE INDEX ON points (h3res13);
Count points per polygon:
SELECT poly.id, COUNT(*) AS n
FROM poly_h3 AS poly
INNER JOIN points AS x
ON poly.h3res13 = x.h3res13
GROUP BY poly.id;

Related

Use St_ExteriorRing, or similar, to fill up 'holes' in Multipolygon geojson

I have an application that reads geojson from an external API. The geojson could be either Polygon or Multipolygon depending on the day.
These are examples of the two
Multipolygon
{"type":"MultiPolygon","coordinates":[[[[-99.51346135949517,49.00303923244011],[-100.15,47.95],[-102.31,47.45],[-108.58,46.38],[-109.18,46.08],[-111.49,44.89],[-112.76,44.42],[-114.63,43.9],[-116.74,43.72],[-118.71000000000001,42.86],[-118.87,42.36],[-118.11,42.11],[-116.35,42.63],[-115.24,42.27],[-114.45,42.12],[-112.6,42.27],[-111.36,42.76],[-110.7,42.98],[-107.75,43.9],[-106.11,43.66],[-104.91,42.66],[-104.77,41.68],[-104.04,40.6],[-103.49,39.34],[-102.87,39.26],[-102.41,38.64],[-102.31,37.41],[-101.91,36.68],[-101.2,36.36],[-100,36.46],[-98.74,38.68],[-98.39,38.95],[-97.53,39.53],[-97.02,39.44],[-96.43,39.32],[-95.05,38.49],[-93.51,37.47],[-93.13,37.15],[-91.08,35.28],[-88.52,34.63],[-86.34,34.86],[-85.31,35.74],[-84.18,36.63],[-83.04,36.79],[-81.59,36.01],[-81.13,35.08],[-80.45,34.34],[-80.32,34.26],[-79.57,33.82],[-78.81808696186206,33.27775502057361],[-78.931,32.928],[-79.294,32.69],[-79.725,32.427],[-80.061,32.203],[-80.21,32.07],[-80.436,31.966],[-80.764,31.633],[-81.09,30.74],[-80.989,30.128],[-80.765,29.498],[-80.349,28.83],[-80.197,28.552],[-80.271,28.232],[-79.788,27.148],[-79.7,26.777],[-79.766,26.228],[-79.812,25.576],[-79.965,25.158],[-80.143,24.859],[-80.331,24.705],[-80.635,24.487],[-81.054,24.361],[-81.782,24.208],[-82.816,24.169],[-83.288,24.221],[-83.28,24.513],[-83.238,24.798],[-83.163,25.012],[-82.313,25.017],[-82.368,25.134],[-82.51,25.288],[-82.632,25.453],[-81.934,25.61],[-82.143,26.09],[-82.324,26.171],[-82.535,26.48],[-82.719,26.892],[-83.105,27.532],[-83.07,27.615],[-83.183,27.781],[-83.185,28.105],[-83.156,28.373],[-83.014,28.465],[-82.99,28.551],[-83.026,28.753],[-83.273,28.827],[-83.421,29.093],[-83.625,29.29],[-83.895,29.642],[-84.073,29.73],[-84.171,29.626],[-84.467,29.438],[-85.018,29.246],[-85.336,29.335],[-85.675,29.436],[-85.767,29.696],[-85.985,29.876],[-86.462,30.068],[-87.771,29.878],[-88.406,29.863],[-88.432,29.714],[-88.695,29.394],[-88.581,29.249],[-88.619,29.022],[-88.937,28.691],[-89.175,28.63],[-89.399,28.577],[-89.712,28.693],[-89.828,28.916],[-90.102,28.755],[-90.3616055222485,28.728920906440788],[-90.59,29.18],[-90.87,30.19],[-90.74,31.24],[-90.18,31.97],[-89.72,32.76],[-90.62,34.34],[-93.28,37.05],[-95,37.73],[-96.23,37.81],[-97.39,37.44],[-98.65,36.55],[-100.01,35.09],[-101.27,34.54],[-102.23,34.53],[-103.01,34.66],[-103.73,34.32],[-103.8,33.75],[-103.99,33.41],[-104.32,32.69],[-105.35,32.06],[-105.9701240038253,31.300129741791523],[-106.325,31.547],[-106.448,31.769],[-107.166,31.772],[-107.78,31.773],[-108.182,31.777],[-108.213,31.305],[-109.08,31.308],[-109.891,31.327],[-110.732,31.321],[-111.103,31.32],[-112.189,31.67],[-112.66904089087537,31.81540516722906],[-112.7,32.69],[-111.95,33.62],[-110.88,34.28],[-109.67,34.89],[-108.43,35.58],[-107.8,36.94],[-107.19,38.87],[-106.27,40.84],[-106.23,41.55],[-106.45,42.34],[-107.19,42.6],[-109.05,42.48],[-110.95,41.14],[-112.4,40.46],[-115.28999999999999,39.87],[-117.96000000000001,39.46],[-119.89,39.24],[-121.74,39.12],[-123.06,39.74],[-123.78,41.24],[-123.28999999999999,43.33],[-121.28999999999999,45.26],[-119.8,46.42],[-118.65,47.32],[-119.25,48],[-120.4,48.34],[-121.84015239078087,48.98691337339889],[-121.659,48.986],[-120.472,48.999],[-119.253,48.999],[-117.737,49.015],[-116.09,48.994],[-114.493,48.985],[-113.116,49.003],[-111.3,48.99],[-109.984,48.991],[-109.64,48.997],[-108.993,48.996],[-107.31,48.993],[-105.605,49.009],[-103.573,48.991],[-101.079,48.988],[-99.687,49.004],[-99.51346135949517,49.00303923244011]],[[-84.43,34.91],[-86.05,34.1],[-86.37,33],[-85.19,32.64],[-84.24,32.79],[-83.28,33.27],[-82.39,33.89],[-82.05,34.63],[-82.23,35.26],[-83.17,35.46],[-84.43,34.91]]],[[[-88.87125436792684,48.17251743575732],[-88.4,47.64],[-88.31,46.8],[-88.89,45.98],[-89.68,45.43],[-90.53,43.89],[-90.58,42.8],[-90.29,42.37],[-89.65,41.69],[-88.48,41.31],[-86.62,41.26],[-83.61,41.42],[-81.71,41.54],[-80.43,42.18],[-80.35384895547264,42.36541123884922],[-80.167,42.399],[-79.353,42.673],[-78.966,42.881],[-79.194,43.433],[-78.597,43.655],[-77.369,43.615],[-76.813,43.628],[-76.377,44.151],[-75.828,44.443],[-75.494,44.756],[-74.876,45.025],[-71.551,45.021],[-71.366,45.294],[-70.884,45.379],[-70.38,45.841],[-70.319,46.114],[-70.03,46.658],[-69.251,47.478],[-68.967,47.467],[-68.967,47.24],[-68.497,47.296],[-68.287,47.417],[-67.785,47.096],[-67.744,46.598],[-67.724,45.726],[-67.403,45.548],[-67.372,45.222],[-67.182,45.297],[-67.043,45.044],[-66.976,44.938],[-66.991,44.84],[-66.86,44.821],[-67.302,44.414],[-67.279,44.152],[-67.925,43.906],[-68.184,43.802],[-68.352,43.817],[-68.538,43.761],[-68.681,43.688],[-68.902,43.581],[-69.149,43.51],[-69.388,43.422],[-69.557,43.333],[-69.738,43.3],[-69.962,43.13],[-70.094,42.939],[-70.041,42.768],[-69.908,42.491],[-69.795,42.31],[-69.595,42.133],[-69.483,42.001],[-69.443,41.721],[-69.548,41.498],[-69.488,41.199],[-69.663,41.018],[-69.946,40.916],[-70.34,40.942],[-70.838,40.931],[-71.244,40.859],[-71.608,40.77],[-72.156,40.586],[-72.567,40.462],[-72.944,40.361],[-73.2,40.303],[-73.648,40.236],[-73.751,39.803],[-73.839,39.533],[-74.085,39.198],[-74.24431116799899,39.091621252336154],[-74.72,40.43],[-74.13,41.32],[-73.27,42.18],[-73.28,42.96],[-73.69,43.37],[-75.34,43.41],[-76.08,42.83],[-76.99,42.2],[-77.99,41.95],[-79.57,41.64],[-81.97,41],[-84.85,40.86],[-88.66,40.91],[-89.93,41.4],[-91.14,41.9],[-91.69,42.6],[-92.39,43.51],[-92.06,44.4],[-91.92,45.58],[-92.15,46.97],[-92.73,48.56],[-92.72981693138672,48.56141140114221],[-92.611,48.546],[-92.049,48.36],[-91.504,48.094],[-90.835,48.281],[-90.646,48.094],[-90.054,48.116],[-89.35,47.985],[-88.87125436792684,48.17251743575732]]]]}
Polygon:
{"type":"Polygon","coordinates":[[[-95.28501052631579,48.998],[-94.58,48.27],[-92.76,47.02],[-90.19,46.11],[-88.05,46.5],[-86.48,47.07],[-85.95569942586447,47.33464695646842],[-85.896,47.31],[-84.848,46.885],[-84.707,46.516],[-84.142,46.508],[-84.043,46.128],[-83.963,46.047],[-83.859,46.044],[-83.754,46.112],[-83.517,46.102],[-83.444,45.977],[-83.53,45.879],[-83.584,45.814],[-82.528,45.336],[-82.199,44.145],[-82.103,43.62],[-82.288,43.266],[-82.307,43.137],[-82.454,42.997],[-82.483,42.619],[-82.704,42.431],[-82.729,42.36],[-83.068,42.27],[-83.189,42.064],[-83.08,41.93],[-83.103,41.858],[-82.7,41.693],[-82.32,41.713],[-81.62,42.066],[-81.185,42.216],[-80.167,42.399],[-79.353,42.673],[-78.966,42.881],[-79.194,43.433],[-78.597,43.655],[-77.369,43.615],[-76.813,43.628],[-76.377,44.151],[-75.828,44.443],[-75.494,44.756],[-75.18869307056579,44.888892498410684],[-75.38,44.11],[-75.46,41.79],[-74.54,40.67],[-73.68141943423038,40.095508592021794],[-73.751,39.803],[-73.839,39.533],[-74.085,39.198],[-74.395,38.991],[-74.504,38.814],[-74.716,38.67],[-74.701,38.442],[-74.762,38.239],[-75.086,37.666],[-75.263,37.548],[-75.356,37.334],[-75.645,36.894],[-75.371,36.199],[-75.12,35.649],[-75.166,35.243],[-75.279,34.984],[-75.429,34.931],[-75.726,34.857],[-76.265,34.375],[-76.54,34.248],[-76.776,34.354],[-76.936,34.358],[-77.521,34.032],[-77.684,33.615],[-77.95,33.516],[-78.163,33.577],[-78.569,33.483],[-78.808,33.309],[-78.931,32.928],[-79.294,32.69],[-79.725,32.427],[-80.061,32.203],[-80.21,32.07],[-80.436,31.966],[-80.764,31.633],[-81.09,30.74],[-80.989,30.128],[-80.765,29.498],[-80.349,28.83],[-80.197,28.552],[-80.271,28.232],[-79.788,27.148],[-79.7,26.777],[-79.766,26.228],[-79.812,25.576],[-79.965,25.158],[-80.143,24.859],[-80.331,24.705],[-80.635,24.487],[-81.054,24.361],[-81.782,24.208],[-82.816,24.169],[-83.288,24.221],[-83.28,24.513],[-83.238,24.798],[-83.163,25.012],[-82.313,25.017],[-82.368,25.134],[-82.51,25.288],[-82.632,25.453],[-81.934,25.61],[-82.143,26.09],[-82.324,26.171],[-82.535,26.48],[-82.719,26.892],[-83.105,27.532],[-83.07,27.615],[-83.183,27.781],[-83.185,28.105],[-83.156,28.373],[-83.014,28.465],[-82.99,28.551],[-83.026,28.753],[-83.273,28.827],[-83.421,29.093],[-83.625,29.29],[-83.895,29.642],[-84.073,29.73],[-84.171,29.626],[-84.467,29.438],[-85.018,29.246],[-85.336,29.335],[-85.675,29.436],[-85.767,29.696],[-85.985,29.876],[-86.462,30.068],[-87.771,29.878],[-88.406,29.863],[-88.432,29.714],[-88.695,29.394],[-88.581,29.249],[-88.619,29.022],[-88.937,28.691],[-89.175,28.63],[-89.399,28.577],[-89.712,28.693],[-89.828,28.916],[-90.102,28.755],[-90.54,28.711],[-90.745,28.711],[-91.083,28.732],[-91.315,28.893],[-91.556,28.983],[-91.655,29.121],[-91.742,29.058],[-92.079,29.114],[-92.164,29.181],[-92.629,29.233],[-93.243,29.441],[-93.588,29.43],[-93.777,29.35],[-93.86371715145437,29.3490574222668],[-94.43,30.11],[-95.48,32.28],[-95.9,34.79],[-97.03,36.61],[-98.25,37.5],[-99.48,37.64],[-100.37,37.51],[-100.95,36.99],[-101.57,36.09],[-102.05,35.51],[-103.01,35.26],[-103.97,35.42],[-104.89,36.02],[-105.46,36.22],[-106.3,35.84],[-104.73,33.68],[-104.99,32.67],[-106.17,32.34],[-107.24,32.43],[-107.23,31.95],[-107.16324568439407,31.771988491717522],[-107.166,31.772],[-107.78,31.773],[-108.182,31.777],[-108.213,31.305],[-109.08,31.308],[-109.891,31.327],[-110.732,31.321],[-111.103,31.32],[-111.5068844073521,31.450165324653078],[-111.32,31.76],[-110.88,32.62],[-111.06,34.11],[-110.37,35.35],[-109.05,36.35],[-108.06,37.78],[-107.21,39.63],[-105.82,41.42],[-105.1,41.94],[-105.06,42.36],[-105.26,42.78],[-105.62,43.15],[-106.56,43.87],[-107.39,44.07],[-109.01,43.9],[-111.36,43.17],[-113.32,43.43],[-114.49,43.16],[-115.82,42.89],[-116.23,42.73],[-118.37,43.02],[-120.51,43.78],[-121.1,44.75],[-119.58,46.35],[-119.09,47.98],[-119.44802702702702,48.999],[-119.253,48.999],[-117.737,49.015],[-116.09,48.994],[-114.493,48.985],[-113.116,49.003],[-111.3,48.99],[-109.984,48.991],[-109.64,48.997],[-108.993,48.996],[-107.31,48.993],[-105.605,49.009],[-103.573,48.991],[-101.079,48.988],[-99.687,49.004],[-98.242,48.996],[-96.798,48.991],[-95.874,48.998],[-95.28501052631579,48.998]],[[-90.2,32.05],[-88.72,30.79],[-88.05,30.65],[-87.21,30.8],[-86.45,31.65],[-85.88,32.11],[-85.02,32.55],[-83.71,32.58],[-82.55,33.06],[-81.69,33.45],[-81.33,34.29],[-80.13,35.88],[-79.91,37.41],[-79.69,38.28],[-80.79,38.96],[-83.11,38.16],[-85.63,38.75],[-88.01,38.14],[-90.04,37.53],[-91.48,36.43],[-92.4,35.03],[-92.58,34.06],[-91.41,32.64],[-90.2,32.05]],[[-109.65,45.06],[-100.16,42.48],[-99.01,41.67],[-97.4,38.75],[-95.53,35.91],[-94.12,35.24],[-93.06,35.66],[-90.26,38.1],[-90.85,40.46],[-92.65,43.69],[-94.58,44.37],[-98.8,44.91],[-102.84,46.18],[-110.55,46.83],[-111.85,46.61],[-112.49,46.05],[-111.89,45.39],[-109.65,45.06]]]}
The polygons almost always have a 'hole' in them, so I use:
ST_SetSRID(St_ExteriorRing(ST_GeomFromGeoJSON(mygeojson))
This works fine for Polygon but not for Multipolygon. I can understand why, as it may be a difficult task to "fill up the holes" of multiple geometries.
Is there a way to solve this so that multipolygons also can use ST_ExteriorRing (or the likes of it)? Conditional solutions are fine, I can find out which are polygons and multipolygons so the solution only needs to be for multipolygon.
One option is to ST_Dump the polygon, apply ST_ExteriorRing, and finally - if necessary - recreate the MultiPolygon with ST_Collect, e.g.
SELECT
ST_Collect(ST_MakePolygon(ST_ExteriorRing(geom)))
FROM (SELECT gid,(ST_Dump(mygeojson::geometry)).* FROM mytable) j
GROUP BY gid;
The query assumes each geometry has an identifier. In this case called gid. In case the geometries have no identifier, you might wanna create one with the window function ROW_NUMBER():
SELECT
ST_Collect(ST_MakePolygon(ST_ExteriorRing(geom)))
FROM (
SELECT
ROW_NUMBER() OVER w AS gid,
(ST_Dump(mygeojson::geometry)).*
FROM mytable
WINDOW w AS (ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
) j
GROUP BY gid;
It gets much shorter if you can live with Polygons ;)
SELECT
ST_MakePolygon(ST_ExteriorRing((ST_Dump(mygeojson::geometry)).geom))
FROM mytable

find number of polygons a line intersects

I am working with SQL Server and spatial types. Have found the very interesting. I have reached a situation and I am not sure how I might work it out. Let me outline
I have 2 points (coordinates) eg. 3,9 and 50, 20 - this is a straight line, joining such.
I have multiple polygons (50 +).
I want to be able to calculate how many polygons that the above line passes through. What I mean by pass through, when I join the 2 coordinates, how many polygons the line intersects? I want to work this out with a SQL query.
Please let me know if not clear - it is difficult to explain!
Based on your coordinates, I'm assuming geometry (as opposed to geography), but the approach should hold regardless. If you have a table dbo.Shapes that has a Shape column of type geometry and each row contains one polygon, this should do:
declare #line geometry = geometry::STLineFromText('LINESTRING(3 9, 50 20)', 0);
select count(*)
from dbo.Shapes as s
where s.shape.STIntersects(#line) = 1;
If you want to know which polygons intersect, just change the count(*) to something more appropriate.

Need polygon intersect counts with buffered polygon neighbors FOR EACH polygon

I'm trying to find whether its possible in purely SQL to generate a table with the number of intersects each polygon in a layer has with its corresponding neighboring polygons(buffered) in a buffered version of the layer.
A rough and flawed version is the following:
For each value in list:
SELECT
Count(*)
INTO
intersectcounts
FROM
parcels,parcelsbuffered
WHERE
parcels.apn = value AND ST_INTERSECT(parcels.geom,parcelsbuffered.geom)
Here the geom is the polygon
I need as result like
intersectscount table
APN COUNT
100 3
101 87
...
...
I could use python loop and modify the query string with a different value in the WHERE clause but I dont think this will have good performance - there are thousands of parcels(polygons)
SELECT parcels.apn, count(*) as intersectcounts
FROM parcels
JOIN parcelsbuffered
ON ST_INTERSECT(parcels.geom, parcelsbuffered.geom)
GROUP BY parcels.apn
You probably want include some validation to remove the parcel intersect with his own buffered version like
(count(*) - 1) as intersectcounts
or
WHERE parcerls.apn <> parcelsbuffered.apn

Spatial SQL query showing parcels containing centroid of building

I am trying to write a query that selects parcels that contain the centroid of a certain building code (bldg_code = 3).
The parcels are listed in the table "city.zoning" and contains a column for a PIN, geometry, and area of each parcel. The table "buildings" contains a column for bldg_type and bldg_code indicating the building type and its corresponding code. The building type of interest for this query has a bldg_code of 3.
So far I've developed a query that shows parcels that interact with the building type of interest:
select a.*
from city.zoning a, username.buildings b
where b.bldg_code = 3 and sdo_anyinteract(a.geom,b.geom) = 'TRUE';
Any ideas?
You can use SDO_GEOM.SDO_CENTROID (documentation) to find the centroid of a geometry.
Note that the centroid provided by this function is the mathematical centroid only and may not always lie inside the geometry, for example, if your polygon is L shaped. SpatialDB Adviser has a good article on this, but here's a quick illustration:
If this isn't a problem for you and you don't need that level of accuracy, just use the built-in, but if you do consider this to be a problem (as I did in the past), then SpatialDB Adviser has a standalone PL/SQL package that corrrectly calculates centroids.
Depending on your performance needs, you could calculate the centroids on-the-fly and just use them in your query directly, or alternatively, add a centroid column to the table and compute and cache the values with application code (best case) or trigger (worst case).
Your query would look something like this:
SELECT a.*
FROM city.zoning a
JOIN username.buildings b ON sdo_contains(a.geom, b.centroid) = 'TRUE'
WHERE b.bldg_code = 3
Note that this is using SDO_CONTAINS on the basis of the a.geom column being spatially indexed and a new column b.centroid that has been added and populated (note - query not tested). If the zoning geometry is not spatially indexed, then you would need to use SDO_GEOM.RELATE, or index the centroid column and invert the logic to use SDO_INSIDE.

Select pair of rows that obey a rule

I have a big table (1M rows) with the following columns:
source, dest, distance.
Each row defines a link (from A to B).
I need to find the distances between a pair using anoter node.
An example:
If want to find the distance between A and B,
If I find a node x and have:
x -> A
x -> B
I can add these distances and have the distance beetween A and B.
My question:
How can I find all the nodes (such as x) and get their distances to (A and B)?
My purpose is to select the min value of distance.
P.s: A and B are just one connection (I need to do it for 100K connections).
Thanks !
As Andomar said, you'll need the Dijkstra's algorithm, here's a link to that algorithm in T-SQL: T-SQL Dijkstra's Algorithm
Assuming you want to get the path from A-B with many intermediate steps it is impossible to do it in plain SQL for an indefinite number of steps. Simply put, it lacks the expressive power, see http://en.wikipedia.org/wiki/Expressive_power#Expressive_power_in_database_theory . As Andomar said, load the data into a process and us Djikstra's algorithm.
This sounds like the traveling salesman problem.
From a SQL syntax standpoint: connect by prior would build the tree your after using the start with and limit the number of layers it can traverse; however, doing will not guarantee the minimum.
I may get downvoted for this, but I find this an interesting problem. I wish that this could be a more open discussion, as I think I could learn a lot from this.
It seems like it should be possible to achieve this by doing multiple select statements - something like SELECT id FROM mytable WHERE source="A" ORDER BY distance ASC LIMIT 1. Wrapping something like this in a while loop, and replacing "A" with an id variable, would do the trick, no?
For example (A is source, B is final destination):
DECLARE var_id as INT
WHILE var_id != 'B'
BEGIN
SELECT id INTO var_id FROM mytable WHERE source="A" ORDER BY distance ASC LIMIT 1
SELECT var_id
END
Wouldn't something like this work? (The code is sloppy, but the idea seems sound.) Comments are more than welcome.
Join the table to itself with destination joined to source. Add the distance from the two links. Insert that as a new link with left side source, right side destination and total distance if that isn't already in the table. If that is in the table but with a shorter total distance then update the existing row with the shorter distance.
Repeat this until you get no new links added to the table and no updates with a shorter distance. Your table now contains a link for every possible combination of source and destination with the minimum distance between them. It would be interesting to see how many repetitions this would take.
This will not track the intermediate path between source and destination but only provides the shortest distance.
IIUC this should do, but I'm not sure if this is really viable (performance-wise) due to the big amount of rows involved and to the CROSS JOIN
SELECT
t1.src AS A,
t1.dest AS x,
t2.dest AS B,
t1.distance + t2.distance AS total_distance
FROM
big_table AS t1
CROSS JOIN
big_table AS t2 ON t1.dst = t2.src
WHERE
A = 'insert source (A) here' AND
B = 'insert destination (B) here'
ORDER BY
total_distance ASC
LIMIT
1
The above snippet will work for the case in which you have two rows in the form A->x and x->B but not for other combinations (e.g. A->x and B->x). Extending it to cover all four combiantions should be trivial (e.g. create a view that duplicates each row and swaps src and dest).