Changes between Version 2 and Version 3 of WKTRasterTutorial01
- Timestamp:
- 06/10/10 13:30:25 (15 years ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
WKTRasterTutorial01
v2 v3 28 28 Launch a command prompt (or a shell under Linux) and make sure you can execute "shp2pgsql". If this works, you should get the help for the shp2pgsql command. If not, make sure the folder where it is located ("PostgreSQL_Installation_Path/bin) is in you PATH environement variable or simply type the complete path to shp2pgsql like this: 29 29 30 {{{ 30 31 >"c:/Program Files/PostgreSQL/8.X/bin/shp2pgsql" 32 }}} 31 33 32 34 shp2pgsql works in two steps: The first step is to create a .sql file that you then have to execute using psql, an application which comes with PostgreSQL. To convert the caribou point shapefile in cariboupoint.sql, we do the following: 33 35 34 >shp2pgsql.exe -s 32198 -I C:\Temp\Pierre\Data\CaribouPoints\cariboupoints.shp > C:\Temp\36 {{{ >shp2pgsql.exe -s 32198 -I C:\Temp\Pierre\Data\CaribouPoints\cariboupoints.shp > C:\Temp\ 35 37 Pierre\Data\CaribouPoints\cariboupoints.sql 38 }}} 36 39 37 40 The -s option tells the loader that the shapefile points are located in the Quebec Lambert spatial reference system which has SRID number 32198 in the PostGIS spatial_ref_sys table. … … 43 46 You can then execute the sql file with psql like this: 44 47 48 {{{ 45 49 >psql -f C:\Temp\Pierre\Data\CaribouPoints\cariboupoints.sql demodb 50 }}} 46 51 47 52 == Visualizing the caribou point in OpenJUMP == … … 57 62 3. In the query field, type the following command: 58 63 64 {{{ 59 65 SELECT id, ST_AsBinary(the_geom) 60 FROM cariboupoints 66 FROM cariboupoints; 67 }}} 61 68 62 69 You should see your caribou observation point appear in the OpenJUMP map window. … … 68 75 To display gdal2wktraster.py help, simply do: 69 76 77 {{{ 70 78 >gdal2wktraster.py -h 79 }}} 71 80 72 81 We downloaded 8 zipped files. If you have 7-Zip installed, you can simply select all the zip files, right-click on them and select "7-Zip->Extract files..." 7-Zip will extract each TIF file in the same folder. You can then create another folder and move them there. Each file is 6000 pixels wide by 6000 pixel high. You can quickly preview the images if you have IrfanView installed. Efficient raster/vector analysis with PostGIS WKT Raster requires the raster files to be split into tiles in the database. Fortunately gdal2wktraster.py has an option to tile the images the size we like. We will tile them into 100x100 pixels resulting in 60 * 60 * 13 = 46800 tiles. gdal2wktraster.py also accepts wildcard so we will be able to load our 8 big raster in one sole command. … … 74 83 To load the raster files in PostGIS WKT Raster, do something like: 75 84 85 {{{ 76 86 >gdal2wktraster.py -r C:\Temp\Pierre\Data\SRTM\tif\*.tif -t srtm_tiled -s 4326 -k 100x100 -I > C:\Temp\Pierre\Data\SRTM\srtm.sql 77 87 }}} 88 78 89 The -r option indicate the series of raster we want to load in the database. 79 90 … … 86 97 The -I option tells the loader to create a spatial index on the raster tile table. If you forget to add this option you can always add the index by executing a SQL command similar to this one: 87 98 99 {{{ 88 100 CREATE INDEX srtm_tiled_gist_idx ON srtm_tiled USING GIST (ST_ConvexHull(rast)); 101 }}} 89 102 90 103 The result of the gdal2wktraster.py command is a 1.8 GB .sql file produced in 1 minute on my brand new Lenovo X201 labtop (Intel Core i5, 1.17 GHz, 3 GB or RAM :-). … … 92 105 The same way we loaded the caribou point sql command file, we will load this sql file using psql: 93 106 107 {{{ 94 108 >psql -f C:\Temp\Pierre\Data\SRTM\srtm.sql demodb 109 }}} 95 110 96 111 This process took less than 4 minutes. You can quickly verify the success of the loading operation by looking at the number of row present in the srtm_tiled table. There should be 46800 rows. … … 98 113 You can then visualise the extent of each of those 46800 rasters by typing the following command in the OpenJUMP "Run Datastore Query" dialog: 99 114 100 SELECT rid, ST_AsBinary(rast::geometry) FROM srtm_tiled 115 {{{ 116 SELECT rid, ST_AsBinary(rast::geometry) FROM srtm_tiled; 117 }}} 101 118 102 119 You can also view the complete area covered by the raster coverage as one geometry by doing this: 103 120 104 SELECT ST_AsBinary(ST_Buffer(ST_Union(rast::geometry), 0.000001)) FROM srtm_tiled 121 {{{ 122 SELECT ST_AsBinary(ST_Buffer(ST_Union(rast::geometry), 0.000001)) FROM srtm_tiled; 123 }}} 105 124 106 125 As you can see, unlike most raster coverage loaded into a GIS, the area covered by this single table raster coverage is not stricly rectangular and the two big missing square areas are not filled with nodata values. Here you have only one table for the whole coverage which is build from many raster files loaded in a single step. You don't need 13 tables to store 13 rasters. We did this with one band SRTM files in TIF format, but it could have been 3 bands BMP files of JPEG and the total size of the coverage does not really matter (PostgreSQL has a limit of 32 Terabytes)... … … 108 127 If you want to go to the pixel level and verify the integrity of the values associated to a sample of the raster, (thanks to Jorge Arevalo and GDAL) you can view a vectorization of one of the tile (vectorizing all the table would be way too long) in OpenJUMP. Just type the following SQL query in the same query dialog: 109 128 129 {{{ 110 130 SELECT ST_AsBinary((ST_DumpAsPolygons(rast)).geom), (ST_DumpAsPolygons(rast)).val 111 131 FROM srtm_tiled 112 WHERE rid=34 132 WHERE rid=34; 133 }}} 113 134 114 135 You will notice that a small area in the upper left corner of the tile is missing. This is the nodata value area. Any analysis function in PostGIS WKT Raster takes the nodata value into account. That means those area will be ommited from the operation. For example in the upcoming intersection operation, if one buffer falls in a nodata value area, st_intersection() and st_intersects() will ignore any part of the raster with nodata values possibly resulting in no intersection at all. … … 116 137 You can verify that your raster has a nodatavalues set and the effective value of the nodata value by doing this query: 117 138 139 {{{ 118 140 SELECT ST_BandHasNodataValue(rast), ST_BandNodataValue(rast) 119 141 FROM srtm_tiled 120 LIMIT 1 142 LIMIT 1; 143 }}} 121 144 122 145 I you want a query to ignore the nodata value set and treat it like any other value, you can always replace any rast argument with ST_SetBandHasNodataValue(rast, FALSE) like in this query: 123 146 147 {{{ 124 148 SELECT ST_AsBinary((ST_DumpAsPolygons(ST_SetBandHasNodataValue(rast, FALSE) )).geom), (ST_DumpAsPolygons(ST_SetBandHasNodataValue(rast, FALSE))).val 125 149 FROM srtm_tiled 126 WHERE rid=XXX 150 WHERE rid=34; 151 }}} 127 152 128 153 == Producing a random point table similar to the caribou shapefile == … … 130 155 Now that we know the extent of our elevation data, we can produce a table similar to the caribou_point one. For this we will use a function that generate a certain number of point geometry inside a polygon. Copy this function in the pgAdmin III query window and execute it: 131 156 132 CREATE OR REPLACE FUNCTION ST_RandomPoints(geom geometry, nb int) 157 {{{ 158 CREATE OR REPLACE FUNCTION ST_RandomPoints(geom geometry, nb int) 133 159 RETURNS SETOF geometry AS 134 160 $$ … … 170 196 $$ 171 197 LANGUAGE 'plpgsql' IMMUTABLE STRICT; 198 }}} 172 199 173 200 You can then generate a fake cariboupoints table with 814 points doing this query: 174 201 202 {{{ 175 203 CREATE TABLE cariboupoints AS 176 204 SELECT generate_series(1,1000) id, 177 205 ST_RandomPoint(the_geom, 1000) the_geom 178 FROM (SELECT ST_Transform(ST_Extent(rast::geometry), 32198) FROM srtm_tiled) foo 206 FROM (SELECT ST_Transform(ST_Extent(rast::geometry), 32198) FROM srtm_tiled) foo; 207 }}} 179 208 180 209 Similarly you can visualise your own caribou point layer with this OpenJUMP query: 181 210 211 {{{ 182 212 SELECT id, ST_AsBinary(the_geom) 183 FROM cariboupoints 213 FROM cariboupoints; 214 }}} 184 215 185 216 Now that we have our two coverages loaded in the database and that we are confident about their values and spatial reference, let's start doing some analysis queries... … … 189 220 This operation is also very straightforward for any casual PostGIS user. The caribou point table is in a spatial reference system suitable for measurements in meters so we can build our buffers in this system and then convert them to the raster one later in order to intersects them. We will make 1 km buffers around our points and save them as a new table. We do this operation in a pgAdmin III query window (not in OpenJUMP): 190 221 222 {{{ 191 223 CREATE TABLE cariboupoint_buffers AS 192 224 SELECT id, ST_Buffer(the_geom, 1000) the_geom 193 FROM cariboupoints 194 225 FROM cariboupoints; 226 }}} 227 195 228 Again you can display your buffers in OpenJUMP with this query: 196 229 230 {{{ 197 231 SELECT id, ST_AsBinary(the_geom) 198 FROM cariboupoint_buffers 199 232 FROM cariboupoint_buffers; 233 }}} 234 200 235 We then reproject our buffers to WGS 84 so we can intersect them with our raster coverage: 201 236 237 {{{ 202 238 CREATE TABLE cariboupoint_buffers_wgs AS 203 239 SELECT id, ST_Transform(the_geom, 4326) the_geom 204 FROM cariboupoint_buffers 205 240 FROM cariboupoint_buffers; 241 }}} 242 206 243 Again the same kind of query to display them in OpenJUMP and confirm that they overlap with the raster coverage and see that now they are a bit squashed: 207 244 245 {{{ 208 246 SELECT id, ST_AsBinary(the_geom) 209 FROM cariboupoint_buffers_wgs 210 247 FROM cariboupoint_buffers_wgs; 248 }}} 249 211 250 We could have done those two queries in one: 212 251 252 {{{ 213 253 CREATE TABLE cariboupoint_buffers_wgs AS 214 254 SELECT id, ST_Transform(ST_Buffer(the_geom, 1000), 4326) the_geom 215 FROM cariboupoints 216 255 FROM cariboupoints; 256 }}} 257 217 258 We then create a spatial index on this last table to make sure the next intersection operation will be done as quickly as possible: 218 259 260 {{{ 219 261 CREATE INDEX cariboupoint_buffers_wgs_geom_idx ON cariboupoint_buffers_wgs USING GIST (the_geom); 220 262 }}} 263 221 264 == Intersecting the caribou buffers with the elevation rasters == 222 265 223 266 Our two coverages are now ready to be intersected. We will use the brand new st_intersection() function and st_intersects() operator which operates directly on raster and geometries by vectorizing only the necessary part of the raster before doing the intersection and is one of the main feature of PostGIS WKT Raster: 224 267 268 {{{ 225 269 CREATE TABLE caribou_srtm_inter AS 226 270 SELECT id, … … 229 273 FROM cariboupoint_buffers_wgs, 230 274 srtm_tiled 231 WHERE st_intersects(rast, the_geom) 275 WHERE st_intersects(rast, the_geom); 276 }}} 232 277 233 278 This query takes about 30 minutes to complete. It can greatly be optimized by invoquing the st_intersection only once with the help of a subquery producing record values that we split later in the main query like this: 234 279 280 {{{ 235 281 CREATE TABLE caribou_srtm_inter AS 236 282 SELECT id, … … 242 288 cariboupoint_buffers_wgs 243 289 WHERE st_intersects(rast, the_geom) 244 ) foo 290 ) foo; 291 }}} 245 292 246 293 This version takes only 17 minutes. Almost half the time spent by the preceding one... This little increase in complexity certainly worths the gain in performance. … … 248 295 Once again you can visualise the result of the query by doing this query in OpenJUMP (you might have to increase RAM allowed to OpenJUMP by changing "-Xmx256M" to "-Xmx1024M" in "OpenJUMPInstallPath/bin/openjump.bat"): 249 296 297 {{{ 250 298 SELECT id, val, ST_AsBinary(the_geom) 251 FROM caribou_srtm_inter 299 FROM caribou_srtm_inter; 300 }}} 252 301 253 302 One approach to make this operation without PostGIS WKT Raster would have been to first convert our 13 rasters to 13 shapefiles, import them in PostGIS and do a traditionnal intersection query between geometries only. The problem is that converting only one of those SRTM file is very difficult... The vector version of only one of those files exceed the limit of 2 GB imposed on shapefiles and the GML equivalent file is more than 10 GB... The WKT Raster approach goes like this: "since it is mostly impossible to convert those raster to vector, load them all into PostGIS as is and only the smallest part of them raster will be vectorized to do the requested operation", this using the very efficient GiST index, the versatility of GDAL and of power of the postGIS vector engine. XXXXXWe will later examine alternative solutions to this particular vectorization problem as well as how we could do the complete raster/vector operation using other software. … … 258 307 The last step of our analysis is to summarize the elevation for each buffer by computing 814 weighted mean elevations. "Weighted mean elevation" means that if the area occupied by pixels with an elevation equal to 234 m is greater than the area occupy pixels with an elevation equal to 56 m, then 234 has a higher weight in the equation (proportional to it relative area).This query should do the work: 259 308 309 {{{ 260 310 CREATE TABLE result01 AS 261 311 SELECT id, … … 263 313 FROM caribou_srtm_inter1 264 314 GROUP BY id 265 ORDER BY id 315 ORDER BY id; 316 }}}