http://www.postgis.us http://www.bostongis.com
http://www.paragoncorporation.com http://www.postgresonline.com
Except where otherwise noted, content on these slides is licensed under a Creative Commons Attribution 4.0 International license.
Pixelated view of the world
Relational view of the world
Costs along a network
hstore needs to be installed before you can use --hstore-all or --hstore
Data from https://mapzen.com/metro-extracts (Chicago) chicago.osm.pbf
osm2pgsql -d presentation -H Y -U postgres -P 5438 -W \
-S default.style --hstore-all chicago.osm.pbf
Rasters are matrixes that you can perform analysis on. They can also be rendered as pretty pictures. In PostGIS land, they live chopped into tiles in a table row in a column type called raster. They are often found dancing with geometries.
We'll start with the serious (what real raster people work with) and move to the playful (what even your toddler can grasp).
Temperature, water fall, climate change
Geometries can be rasterized
Rasters are stored in table rows in a column of data type raster.
CREATE TABLE elevated(rid serial primary key, rast raster);
Rasters (especially those covering a large expanse of land) can be big so we chop them into smaller bits called tiles for easier analysis.
Tiles covering continguous non-overlapping areas of space with same kind of information we call:
A COVERAGE
Tiles are further broken down into pixels (or cells), organized into a matrix.
Columns X | |
Rows Y |
Numbering starts at the upper-left corner
Longitude | |
Latitude |
Note how Y coordinates are generally in reverse order of Pixel row numbering.
Think of each band as a separate matrix storing a particular theme of data and particular numeric range.
One band can store elevation
Another temperature
Another vegetation
Another number of pizza restaurants in each area
If you just care about pretty pictures you can have a 4-band raster representing RGBA channels (bands) in your picture
Bands are classified by the range of numeric data they can store and each band in a given raster can store a different range type of data.
Bit Unsigned Integers (BUI): 2BUI, 4BUI, 8BUI, 16BUI, 32BUI
Bit Signed Integers (SI): 8BSI, 16BSI, 32BSI
Bit Floats (BF) - 32BF, 64BF
Each pixel has slots for number of bands
3-banded raster means 3 values per pixel
GDAL: http://www.gdal.org
PostGIS raster is built on Gdal and wraps a lot of the functions of GDAL in an SQL wrapper
Commonly used command-line of GDAL
raster2pgsql is a command-line tool packaged with PostGIS 2+ that allows loading data from various raster formats into PostGIS raster format. It generates a sql load file or you can pipe directly to PostgreSQL with psql.
RELEASE: 2.2.0dev GDAL_VERSION=111 (r12973) USAGE: raster2pgsql [<options>] <raster>[ <raster>[ ...]] [[<schema>.]<table>] Multiple rasters can also be specified using wildcards (*,?). OPTIONS: -s <srid> Set the SRID field. Defaults to 0. If SRID not provided or is 0, raster's metadata will be checked to determine an appropriate SRID. -b <band> Index (1-based) of band to extract from raster. For more than one band index, separate with comma (,). Ranges can be defined by separating with dash (-). If unspecified, all bands of raster will be extracted. -t <tile size> Cut raster into tiles to be inserted one per table row. <tile size> is expressed as WIDTHxHEIGHT. <tile size> can also be "auto" to allow the loader to compute an appropriate tile size using the first raster and applied to all rasters. -P Pad right-most and bottom-most tiles to guarantee that all tiles have the same width and height. -R Register the raster as an out-of-db (filesystem) raster. Provided raster should have absolute path to the file (-d|a|c|p) These are mutually exclusive options: -d Drops the table, then recreates it and populates it with current raster data. -a Appends raster into current table, must be exactly the same table schema. -c Creates a new table and populates it, this is the default if you do not specify any options. -p Prepare mode, only creates the table. -f <column> Specify the name of the raster column -F Add a column with the filename of the raster. -n <column> Specify the name of the filename column. Implies -F. -l <overview factor> Create overview of the raster. For more than one factor, separate with comma(,). Overview table name follows the pattern o_<overview factor>_<table>. Created overview is stored in the database and is not affected by -R. -q Wrap PostgreSQL identifiers in quotes. -I Create a GIST spatial index on the raster column. The ANALYZE command will automatically be issued for the created index. -M Run VACUUM ANALYZE on the table of the raster column. Most useful when appending raster to existing table with -a. -C Set the standard set of constraints on the raster column after the rasters are loaded. Some constraints may fail if one or more rasters violate the constraint. -x Disable setting the max extent constraint. Only applied if -C flag is also used. -r Set the constraints (spatially unique and coverage tile) for regular blocking. Only applied if -C flag is also used. -T <tablespace> Specify the tablespace for the new table. Note that indices (including the primary key) will still use the default tablespace unless the -X flag is also used. -X <tablespace> Specify the tablespace for the table's new index. This applies to the primary key and the spatial index if the -I flag is used. -N <nodata> NODATA value to use on bands without a NODATA value. -k Skip NODATA value checks for each raster band. -E <endian> Control endianness of generated binary output of raster. Use 0 for XDR and 1 for NDR (default). Only NDR is supported at this time. -V <version> Specify version of output WKB format. Default is 0. Only 0 is supported at this time. -e Execute each statement individually, do not use a transaction. -Y Use COPY statements instead of INSERT statements. -G Print the supported GDAL raster formats. -? Display this help screen.
What kind of rasters can we load?
raster2pgsql -G
Should give you something like
Supported GDAL raster formats: Virtual Raster GeoTIFF National Imagery Transmission Format Raster Product Format TOC format ECRG TOC format Erdas Imagine Images (.img) : Ground-based SAR Applications Testbed File Format (.gff ELAS Arc/Info Binary Grid Arc/Info ASCII Grid GRASS ASCII Grid SDTS Raster DTED Elevation Raster Portable Network Graphics JPEG JFIF In Memory Raster : Graphics Interchange Format (.gif) Envisat Image Format Maptech BSB Nautical Charts X11 PixMap Format MS Windows Device Independent Bitmap SPOT DIMAP AirSAR Polarimetric Image RadarSat 2 XML Product :
Yes pictures have meta data, but don't care much about it as far as loading goes
gdalinfo Chicago_sunrise_1.jpg
Size is 7550, 2400 Coordinate System is `' Image Structure Metadata: COMPRESSION=JPEG INTERLEAVE=PIXEL SOURCE_COLOR_SPACE=YCbCr Corner Coordinates: Upper Left ( 0.0, 0.0) Lower Left ( 0.0, 2400.0) Upper Right ( 7550.0, 0.0) Lower Right ( 7550.0, 2400.0) Center ( 3775.0, 1200.0) Band 1 Block=7550x1 Type=Byte, ColorInterp=Red Overviews: 3775x1200, 1888x600, 944x300 Image Structure Metadata: COMPRESSION=JPEG Band 2 Block=7550x1 Type=Byte, ColorInterp=Green Overviews: 3775x1200, 1888x600, 944x300 Image Structure Metadata: COMPRESSION=JPEG Band 3 Block=7550x1 Type=Byte, ColorInterp=Blue Overviews: 3775x1200, 1888x600, 944x300 Image Structure Metadata: COMPRESSION=JPEG
Don't waste your time with indexes and constraints. Don't bother tiling either
raster2pgsql -e -F -Y pics/*.jpg po.chicago_pics | psql -U postgres -d presentation
For database snobs who feel rasters and gasp! pictures have no place in a database. Use the -R switch
Warning: path you register must be accessible by postgres daemon and for PostGIS 2.1.3+ , 2.0.6+ need to set POSTGIS_ENABLE_OUTDB_RASTERS
environment variable. For 2.2+ have option of GUCs.
raster2pgsql -R -e -F -Y /fullpath/pics/*.jpg po.chicago_pics \
| psql -U postgres -d presentation
gdalinfo chicago-w.DEM
Size is 1201, 1201 Coordinate System is: GEOGCS["NAD27", DATUM["North_American_Datum_1927", SPHEROID["Clarke 1866",6378206.4,294.978698213898 AUTHORITY["EPSG","7008"]], AUTHORITY["EPSG","6267"]], PRIMEM["Greenwich",0, AUTHORITY["EPSG","8901"]], UNIT["degree",0.0174532925199433, AUTHORITY["EPSG","9108"]], AUTHORITY["EPSG","4267"]] Origin = (-88.000416666666666,42.000416666666666) Pixel Size = (0.000833333333333,-0.000833333333333) Metadata: AREA_OR_POINT=Point Corner Coordinates: Upper Left ( -88.0004167, 42.0004167) Lower Left ( -88.0004167, 40.9995833) Upper Right ( -86.9995833, 42.0004167) Lower Right ( -86.9995833, 40.9995833) Center ( -87.5000000, 41.5000000) Band 1 Block=1201x1201 Type=Int16, ColorInterp=Undefined NoData Value=-32767 Unit Type: m
Indexes are important, spatial ref is important, constraints are important too
And you want to tile cause it's gonna be a coverage where fast analysis of small areas is important.
raster2pgsql -I -C -e -F -Y -t auto -s 4267 dems/*.DEM po.chicago_dem \
| psql -U postgres -d presentation
Lots of options, but first a warning.
PostGIS 2.1.3+ and 2.0.6+, security locked down, so you'll need to set: POSTGIS_GDAL_ENABLED_DRIVERS
to use built-in ST_AsPNG etc functions.
PostGIS 2.2+ allow to set using GUCS
Wrap your query in ST_AsPNG or ST_AsGDALRaster etc.
SELECT oid, lowrite(lo_open(oid, 131072), png) As num_bytes
FROM
( VALUES (lo_create(0),
ST_AsPNG( (SELECT rast FROM po.chicago_pics
WHERE filename = 'Chicago_sunrise_1.jpg')
)
) ) As v(oid,png);
oid | num_bytes ---------+----------- 9166618 | 16134052
\lo_export 9166618 'C:/temp.png'
We have a minimalist viewer proof of concept https://github.com/robe2/postgis_webviewer to view most of these
We use it a lot to quickly view adhoc queries that return one raster image and to generate pics in PostGIS docs.
We have a minimalist all encompassing Node.JS web server https://github.com/robe2/node_postgis_express
This we'll use cause its pretty easy to setup and can do all.
gdal_translate and gdalwarp are most popular.
gdal_translate -of PNG -outsize 10% 10% \
"PG:host=localhost port=5432 dbname='presentation'
user='postgres' password='whatever'
schema=po table=chicago_pics mode=2
where='filename=\'Chicago_sunrise_1.jpg\'" test.png
gdalwarp -s_srs "EPSG:4326" \
-t_srs "EPSG:2163" \
PG:"host='localhost' port='5432' dbname='presentation'
user='postgres' password='whatever'
schema='po' table='chicago_dem'
where='ST_Intersects(rast, ST_Transform(
ST_MakeEnvelop(-87.527,41.8719, -87.950, 41.9000,4326),
4267) )'
mode=2" dem_sub.tif
Any language that can run queries, output binaries and dump to file system will do.
CREATE OR REPLACE FUNCTION
write_file (param_bytes bytea, param_filepath text)
RETURNS text
AS $$
f = open(param_filepath, 'wb+')
f.write(param_bytes)
return param_filepath
$$ LANGUAGE plpython3u;
Let's output 120px wide thumbnails into a server folder
SELECT write_file(ST_AsPNG(ST_Resize(rast,
(least(ST_Width(rast), 120))::int,
(least(ST_Width(rast), 120.0) /
ST_Width(rast)*ST_Height(rast))::int )
),
'C:/pics/thumb_' || filename
)
FROM po.chicago_pics ;
Raster being served with a mix of geometry
AKA: How to make your new pictures look really old
SELECT ST_AsPNG(ST_Band(rast,1)) As rast
FROM po.chicago_pics
WHERE filename='Full_chicago_skyline.jpg';
Before | After |
SELECT ST_AsPNG(
ST_Reclass(ST_Band(rast,1),1,'0-70:255, 71-189:100, 190-255:200', '8BUI',255)
)
FROM po.chicago_pics
WHERE filename = 'Full_chicago_skyline.jpg';
Before | After |
There are good uses for this, but this is questionably not one of them.
SELECT ST_AsPNG(ST_Band(rast,'{3,2,1}'::integer[])) As png
FROM po.chicago_pics
WHERE filename='Full_chicago_skyline.jpg';
Before | After |
You would do this with terrain data, but you could do it with pictures and create a charcoal drawing. Gives you relative measure of difference between max and min. Bears a striking resemblance to what you get when applying the ST_Range4MA mapalgebra callback function.
SELECT ST_AsPNG( ST_Roughness(rast,1, NULL::raster, '8BUI'::text) )
FROM chicago_pics
WHERE filename ILIKE 'Mona%';
Before | After |
Designed for elevation data (gives hypothetical illumination), but go ahead and apply to your pictures and create a stone impression.
SELECT ST_AsPNG( ST_HillShade(rast,1, NULL::raster, '8BUI'::text, 90) ) FROM chicago_pics WHERE filename ILIKE 'Mona%';
SELECT ST_AsPNG( ST_HillShade(rast,1, NULL::raster, '8BUI'::text, 315,30,150) ) FROM chicago_pics WHERE filename ILIKE 'Mona%';
Default (315 azimuth, 45 altitude, 255 max bright) | 315, 30, 150 |
Againt designed for elevation data, Returns the aspect (in degrees by default) of an elevation raster band.
SELECT ST_AsPNG( ST_Aspect(rast,1, NULL::raster, '8BUI'::text) )
FROM chicago_pics
WHERE filename ILIKE 'Mona%';
Operations done on a set of pixels (a neighborhood) where the value returned by the operation becomes the new value for the center pixel. The simplest operation works on a single cell (a 0-neighborhood).
In PostGIS the operation is expressed either as a PostgreSQL algebraic expression, or a Postgres callback function that takes an n-dimensional matrix of pixel values.
PostGIS has several built-in mapalgebra call-backs, but you can build your own. PostGIS packaged ones all have 4MA in them: 4MA means for Map Algebra
(ST_Min4MA, ST_Max4MA, ST_Mean4MA, ST_Range4MA (very similar to ST_Roughness), ST_InvDistWeight4MA, ST_Sum4MA)).
Default neighborhood is 0 distance from pixel in x direction, and 0 distance in y direction.
SELECT ST_AsPNG( ST_MapAlgebra(
ARRAY[ROW(rast, 1)]::rastbandarg[],
'ST_Min4MA(double precision[], int[], text[])'::regprocedure,
'8BUI'::text, 'INTERSECTION'::text,
NULL::raster, 1,1) )
FROM chicago_pics
WHERE filename ILIKE 'Mona%';
Min | Mean | Max |
SELECT ST_AsPNG(ST_AddBand(NULL::raster,
array_agg(ST_MapAlgebra(
ARRAY[ROW(rast, i)]::rastbandarg[],
'ST_Min4MA(double precision[], int[], text[])'::regprocedure,
'8BUI'::text, 'INTERSECTION'::text,
NULL::raster, 1,1
) ) ) )
FROM chicago_pics CROSS JOIN generate_series(1,3) As i
WHERE filename ILIKE 'Mona%';
Before | Min | Max |
SELECT ST_Clip(rast, ST_Buffer(ST_Centroid(rast::geometry),100))
FROM chicago_pics
WHERE filename='Full_chicago_skyline.jpg';
SELECT ST_SetBandNoDataValue(
ST_SetBandNoDataValue(
ST_SetBandNoDataValue(
ST_Clip(rast,
ST_Buffer(ST_Centroid(rast::geometry),200) ),
1,0),2,0),3,0)
FROM chicago_pics
WHERE filename='Full_chicago_skyline.jpg';
SELECT ST_Union(
ST_SetUpperLeft(
ST_Resize(rast,i*0.3,i*0.3),i*150,i
)
)
FROM chicago_pics CROSS JOIN generate_series(1,3) As i
WHERE filename='Full_chicago_skyline.jpg';
SQL should be used for REAL analysis and not to entertain your kids.
SELECT ST_Value(rast,1,loc)
FROM po.chicago_dem As d
INNER JOIN
ST_Transform(
ST_SetSRID(ST_Point(-87.627,41.8819),4326),
4267) As loc
ON ST_Intersects(d.rast, loc);
181
Let's compare with Wikipedia's answer http://en.wikipedia.org/wiki/Chicago
Elevation [1](mean) 594 ft (181 m) Highest elevation – near Blue Island 672 ft (205 m) Lowest elevation – at Lake Michigan 578 ft (176 m)
Clip first to isolate region of interest
SELECT (h.hist).*
FROM ST_Transform(
ST_Buffer(
ST_Point(-87.627,41.8819)::geography,
5000)::geometry,4267
) As loc,
LATERAL
(SELECT ST_Histogram(ST_Union(ST_Clip(rast,loc)),1,5) As hist
FROM po.chicago_dem As d
WHERE ST_Intersects(d.rast, loc) ) As h;
min | max | count | percent -----+-----+-------+---------------------- 166 | 171 | 5 | 0.00040983606557377 171 | 176 | 8 | 0.000655737704918033 176 | 181 | 7307 | 0.598934426229508 181 | 186 | 4414 | 0.361803278688525 186 | 191 | 466 | 0.0381967213114754 (5 rows)
Same exercise as histogram, but with Quants
SELECT (h.quant).*
FROM ST_Transform(
ST_Buffer(
ST_Point(-87.627,41.8819)::geography,
5000)::geometry,4267
) As loc,
LATERAL
(SELECT
ST_Quantile(ST_Union(ST_Clip(rast,loc)),1,
'{0.1,0.5,0.75}'::float[]) As quant
FROM po.chicago_dem As d
WHERE ST_Intersects(d.rast, loc) ) As h;
quantile | value ----------+------- 0.1 | 176 0.5 | 178 0.75 | 181 (3 rows)
Just like geometries we can transform raster data from one projection to another. Let's transform to web mercator to match our OSM chicago data
CREATE TABLE po.chicago_dem_wmerc AS
SELECT rid, ST_Transform(rast, 900913) As rast
FROM po.chicago_dem;
SELECT AddRasterConstraints('po'::name,
'chicago_dem_wmerc'::name, 'rast'::name);
After plain vanilla transformation
SELECT r_table_name, srid, scale_x::numeric(10,5),
scale_y::numeric(10,5), blocksize_x As bx, blocksize_y As by,
same_alignment As sa
FROM raster_columns
WHERE r_table_name LIKE 'chicago_dem%';
r_table_name | srid | scale_x | scale_y | bx | by | sa ------------------+--------+---------+----------+-----+-----+---- chicago_dem_wmerc | 900913 | | | 85 | 113 | f chicago_dem | 4267 | 0.00083 | -0.00083 | 100 | 100 | t
oh oh, new tiles have inconsistent alignment and scale
WITH ref As ( SELECT ST_Transform(rast,900913) As rast
FROM po.chicago_dem LIMIT 1)
SELECT d.rid, ST_Transform(d.rast, ref.rast, 'Lanczos') As rast
INTO po.chicago_dem_wmerc
from ref CROSS JOIN po.chicago_dem As d;
We arbitrarily picked first transformed raster to align with
After we repeat AddRasterConstraints
and rerun our raster_columns query
r_table_name | srid | scale_x | scale_y | bx | by | sa -------------------+--------+-----------+------------+-----+-----+---- chicago_dem_wmerc | 900913 | 109.92805 | -109.92805 | 85 | 114 | t chicago_dem | 4267 | 0.00083 | -0.00083 | 100 | 100 | t
CREATE INDEX idx_chicago_dem_wmerc_rast_gist
ON po.chicago_dem_wmerc
USING gist
(st_convexhull(rast));
DEMS are often not convertable to standard viewing formats like PNG, JPG, or GIF because their band types are often 16BUI, BSI and so forth rather than 8BUI.
With the beauty that is ST_ColorMap, you can change that.
INTERPOLATE
, NEAREST
, EXACT
.
There are: grayscale, pseudocolor, fire, bluered predefined in PostGIS. bluered goes from low of blue to pale white to red.
SELECT ST_ColorMap(ST_Union(rast,1),'bluered') As rast_4b
FROM po.chicago_dem_wmerc
WHERE ST_DWithin(
ST_Transform(ST_SetSRID(ST_Point(-87.627,41.8819),4326),900913),
rast::geometry,5000);
You can define mappings yourself
SELECT ST_ColorMap(ST_Union(rast,1),'100% 255 0 0
75% 200 0 0
50% 100 0 0
25% 50 0 0
10% 10 0 0
nv 0 0 0', 'INTERPOLATE') As rast_4b
FROM po.chicago_dem_wmerc
WHERE ST_DWithin(
ST_Transform(ST_SetSRID(ST_Point(-87.627,41.8819),4326),900913),
rast::geometry,5000);
Fixed set of colors snap to nearest percentile
SELECT ST_ColorMap(ST_Union(rast,1),'100% 255 0 0
75% 200 0 0
50% 100 0 0
25% 50 0 0
10% 10 0 0
nv 0 0 0', 'NEAREST') As rast_4b
FROM po.chicago_dem_wmerc
WHERE ST_DWithin(
ST_Transform(ST_SetSRID(ST_Point(-87.627,41.8819),4326),900913),
rast::geometry,5000);
WITH ref_D AS (SELECT name, way,
ST_Transform(ST_SetSRID(ST_Point(-87.627,41.8819),4326),900913) As loc
FROM po.planet_osm_roads
ORDER BY ST_Transform(
ST_SetSRID(
ST_Point(-87.627,41.8819),4326),900913) <-> way LIMIT 5 ),
ref AS (SELECT name, way FROM ref_d
ORDER BY ST_Distance(way,loc) )
SELECT ref.name,
ST_AsText(
ST_LineMerge(
ST_Collect(
ST_Translate(ST_Force3D((r.gv).geom), 0,0, (r.gv).val)
)
)
) As wktgeom
FROM
ref , LATERAL (SELECT ST_Intersection(rast,way) As gv
FROM po.chicago_dem_wmerc As d
WHERE ST_Intersects(d.rast, ref.way) ) As r
GROUP BY ref.name;
name | wktgeom ------------------------+------------------------------------------------------- North State Street | LINESTRING Z (-9754688.09 5143501.29 179,-9754687.4267 9796 5143457.79342028 179,-9754686.69 5143409.47 180,-9754684.13704138 5143347.8 6536781 180,-9754683.17 5143324.53 181) ... East Madison Street | LINESTRING Z (-9754312.92 5143334.38 181,-9754328.32 5 143334.22 181,-9754407.28 5143333.33 181,-9754501.17 5143330.48 181,-9754587.69 5143329.8 181,-9754652.86 5143328.67 181,-9754666.75 5143327.67 181,-9754683.17 5143324.53 181)... State Street Subway | MULTILINESTRING Z ((-9754692.86 5143480.52 179,-975469 2.09079665 5143457.79342028 179,-9754688.37017403 5143347.86536781 180,-9754680. 91 5143127.45 181),(-9754671.2 5143129.08 181,-9754677.91235681 5143347.86536781 180,-9754681.28496038 5143457.79342028 179,-9754681.95 5143479.47 179)) .. East Washington Street | LINESTRING Z (-9754688.09 5143501.29 179,-9754591.31 5 143501.57 179,-9754538.75 5143500.92 179,-9754504.9 5143501.11 179,-9754410.36 5 143501.83 179,-9754316.66 5143502.34 179,-9754301.88 5143502.49 179,-9754289.33 5143502.58 179,-9754283.02 5143502.6 179) ... (4 rows)
SELECT ST_AsPNG(ST_AsRaster(
ST_Buffer(
ST_GeomFromText('LINESTRING(50 50,150 150,150 65, 30 20)'),
10,'join=bevel'),
200,200,ARRAY['8BUI', '8BUI', '8BUI'],
ARRAY[118,154,118], ARRAY[0,0,0]));
WITH cte AS (SELECT row_number() OVER() As rn, way As geom,
ST_XMax(way) - ST_XMin(way) As width, ST_YMax(way) - ST_YMin(way) As height,
ST_Extent(way) OVER() As full_ext
FROM po.planet_osm_polygon
WHERE admin_level='8' ),
ref AS (SELECT ST_AsRaster(ST_SetSRID(full_ext, ST_SRID(geom)),
((ST_XMax(full_ext) - ST_XMin(full_ext))/
(ST_YMax(full_ext) - ST_YMin(full_ext))*600)::integer,
600,ARRAY['8BUI'],
ARRAY[255], ARRAY[255]) AS rast FROM cte WHERE rn = 1 )
SELECT ST_AsPNG(ST_ColorMap(ST_Union(ST_AsRaster(geom, ref.rast,
ARRAY['8BUI'],
ARRAY[rn*2], ARRAY[255]) ), 'pseudocolor') )
FROM cte CROSS JOIN ref;
Most popular is ST_DumpAsPolygons which returns a set of geomval (composite consisting of a geometry named geom and a pixel value called val).
SELECT (g).val, ST_Union((g).geom) As geom
FROM
(SELECT ST_DumpAsPolygons(ST_Clip(rast,loc) ) As g
FROM po.chicago_dem As d
INNER JOIN
ST_Transform(
ST_Buffer(
ST_SetSRID( ST_Point(-87.627,41.8819),4326)::geography,
500 )::geometry,
4267) As loc
ON ST_Intersects(d.rast, loc) ) AS f
GROUP BY (g).val;
Geometries are stand-alone selfish creatures.
They only look out for number one.
A plot of land overlaps another.
Does geometry care?
No. C'est la vie
People care. We've got boundaries. My land is not your land. Stay off my lawn.
Who can bring order to our world?
Topology can.
Topology views the world as a neatly ordered network of faces, edges, and nodes. Via relationships of these primitive elements, we form recognizable things like parcels, roads, and political boundaries.
An edge is a linestring that does not overlap other edges. It can only touch other edges at the end points.
Nodes are start and end points of edges or are isolated points that are not part of any edge.
A face is a polygon that gets generated when a set of edges form a closed ring.
In standard PostGIS topology, the polygon is never actually stored, but computed. Only the minimum bounding rectangle is stored.
Edges are the numbered linestrings, nodes are the numbered round balls, and faces are the areas with *. The four faces together can be used to construct a topogeometry we can call colorado.
A topogeometry is like a geometry, except it has respect for its foundations. It understands it is not sitting by itself in outerspace. It understands it is made up of other elements which may be shared by other geometries. It understands its very definition is the cumulative definition of others.
It's nice to be a regular old geometry sometimes, and topogeometry has got a cast to make it so. topo::geometry
or geometry(topo)
.
A topogeometry is composed of pointers to topology primitives or other topogeometries. It is defined via the relations
If you haven't already
CREATE EXTENSION postgis_topology;
SELECT CreateTopology('topo_chicago',900913);
You should now have a database schema called topo_chicago with several tables.
topo_chicago has a set of tables you'll find in all topology generated schemas
CREATE TABLE
po.chicago_boundaries(id serial primary key, name text);
SELECT AddTopoGeometryColumn('topo_chicago', 'po',
'chicago_boundaries', 'topo', 'POLYGON') As layer_id;
1
INSERT INTO po.chicago_boundaries(name, topo)
WITH ref AS (SELECT way As ref
FROM po.planet_osm_polygon
WHERE admin_level = '8' AND name = 'Chicago')
SELECT name, toTopoGeom(ST_Union(way), 'topo_chicago', 1)
FROM (SELECT name, way FROM po.planet_osm_polygon
WHERE admin_level = '8') AS b
INNER JOIN ref
ON (ST_Intersects(b.way,ref.ref))
GROUP BY name;
SELECT name, topo::geometry As geom
FROM po.chicago_boundaries;
Topos are aware of their shared existence. Edges remain shared.
Geometries are self-centered. They don't care about their neighbors.
They overlap and are not respectful of each other.
SELECT name, ST_Simplify(topo::geometry,1000) As geom
FROM po.chicago_boundaries;
Edges that were shared are still shared
SELECT name, ST_Simplify(topo,1000) As geom
FROM po.chicago_boundaries;
pgRouting is primarily used for building routing applications. E.g road directions, biking trail guide etc.
Refer to our quick start guide listed on this page: http://www.postgis.us/pgopen2014
CREATE EXTENSION pgrouting;
SELECT * FROM pgr_version();
version | tag | build | hash | branch | boost ---------+--------+-------+---------+--------+-------- 2.0.0 | v2.0.0 | 3 | fbbaa2a | master | 1.54.0
We'll demonstrate osm2po since it's more cross-platform
Your shell-script line should look something like
java -Xmx512m -jar osm2po-core-4.8.8-signed.jar prefix=hh tileSize=x chicago_illinois.osm.pbf
You should now have a folder called hh in your osm2po folder, and should have an sql file hh_2po_4pgr.sql
Load this up into your database with psql. Note that osm2po loads data in wgs 84 long lat geometry (4326) so tolerance units for all pgrouting will be in degrees.
Osm2Po doesn't create a vertices table, but we can create one from the table it creates. This table is needed by some pgrouting functions
SELECT pgr_createVerticesTable('hh_2po_4pgr', 'geom_way', 'source', 'target')
NOTICE: PROCESSING: NOTICE: pgr_createVerticesTable('hh_2po_4pgr','geom_way','source','target','true') NOTICE: Performing checks, pelase wait ..... NOTICE: Populating public.hh_2po_4pgr_vertices_pgr, please wait... NOTICE: -----> VERTICES TABLE CREATED WITH 338965 VERTICES NOTICE: FOR 489747 EDGES NOTICE: Edges with NULL geometry,source or target: 0 NOTICE: Edges processed: 489747 NOTICE: Vertices table for table public.hh_2po_4pgr is: public.hh_2po_4pgr_vertices_pgr NOTICE: ---------------------------------------------- Total query runtime: 42975 ms.
pgr_analyzeGraph looks for dead ends and other anomalies and populates fields in hh_2po_4pgr_vertices_pgr. Tolerance are in units of your spatial ref sys. Takes a while
SELECT pgr_analyzeGraph('hh_2po_4pgr', 0.000001,'geom_way',
'id', 'source', 'target','true');
OK120,322ms
CREATE OR REPLACE VIEW vw_routing
AS
SELECT id, osm_id, osm_name, osm_meta, osm_source_id, osm_target_id,
clazz, flags, source, target, km, kmh, cost,
cost as length, reverse_cost, x1,
y1, x2, y2, geom_way As the_geom
FROM hh_2po_4pgr;
So we really can't go from arbitrary point to point, have to go from node to node. So we found a node using osm2po mini webservice: http://localhost:8888/Osm2poService
Shortest Path A-Star function that takes as input:
Output is order of travel: seq, id1: node id, id2 edge id
SELECT *
FROM pgr_astar('SELECT id, source, target, cost,
x1,y1,x2,y2, reverse_cost FROM vw_routing',
159944, 142934, true, true) As r;
seq | id1 | id2 | cost -----+--------+--------+-------------- 0 | 159944 | 236443 | 0.0020273041 1 | 334003 | 482441 | 0.0027647596 2 | 334007 | 482442 | 0.0010009945 3 | 334006 | 155460 | 0.0025687038 4 | 142934 | -1 | 0
One-ways are considered
SELECT r.seq, r.id1 As node,
s.id As edge, s.osm_name, s.cost, s.km, s.kmh
FROM pgr_astar('SELECT id, source, target, cost,
x1,y1,x2,y2, reverse_cost FROM vw_routing',
159944, 142934, true, true) As r INNER JOIN
vw_routing AS s ON r.id2 = s.id
ORDER BY r.seq;
seq | node | edge | osm_name | cost | km | kmh ----+--------+--------+------------------+--------------+-------------+----- 0 | 159944 | 236443 | West 36th Street | 0.0020273041 | 0.10136521 | 50 1 | 334003 | 482441 | South 58th Court | 0.0027647596 | 0.13823798 | 50 2 | 334007 | 482442 | South 58th Court | 0.0010009945 | 0.050049722 | 50 3 | 334006 | 155460 | West 35th Street | 0.0025687038 | 0.102748156 | 40
One-ways are ignored
SELECT r.seq, r.id1 As node,
s.id As edge, s.osm_name, s.cost, s.km, s.kmh
FROM pgr_astar('SELECT id, source, target, cost,
x1,y1,x2,y2, reverse_cost FROM vw_routing',
159944, 142934, false, false) As r INNER JOIN
vw_routing AS s ON r.id2 = s.id
ORDER BY r.seq;
seq | node | edge | osm_name | cost | km | kmh -----+--------+--------+-------------------+--------------+-------------+----- 0 | 159944 | 236443 | West 36th Street | 0.0020273041 | 0.10136521 | 50 1 | 334003 | 236444 | West 36th Street | 0.0020233293 | 0.101166464 | 50 2 | 333993 | 482429 | South 59th Avenue | 0.0027747066 | 0.13873532 | 50 3 | 333996 | 482428 | South 59th Avenue | 0.0010170189 | 0.050850946 | 50