GDAL/OGR Guide
Inspecting and Loading the Wildlife Spatial Datasets
GDAL/OGR Guide: Inspecting and Loading the Wildlife Spatial Datasets
1. Purpose
This guide documents the workflow used to inspect the supplied GIS files, identify their geometry and coordinate reference systems, detect geometry inconsistencies, and prepare the data for loading into PostgreSQL/PostGIS.
The PostgreSQL target used in the workflow was:
1
2
3
4
5
Database: courses
Schema: miller
User: ivan
Host: localhost
Port: 5432
The main tools used were:
1
2
3
ogrinfo
ogr2ogr
psql
The workflow was:
1
2
3
4
5
6
7
8
9
10
11
12
13
Source files
↓
ogrinfo inspection
↓
Check geometry / CRS / attributes
↓
Detect geometry inconsistencies when import fails
↓
Adjust ogr2ogr options where necessary
↓
Import into PostgreSQL/PostGIS
↓
Verify imported tables
2. Understanding the Shapefile components
Several datasets are supplied as groups of files rather than as a single file.
For example:
1
2
3
4
5
6
baea_nests.shp
baea_nests.shx
baea_nests.dbf
baea_nests.prj
baea_nests.sbn
baea_nests.sbx
These files belong to the same Shapefile dataset.
2.1 .shp
The .shp file contains the actual geometry.
Examples:
1
2
3
Point
LineString
Polygon
This is normally the file passed to ogrinfo and ogr2ogr.
Example:
1
ogrinfo -so -al baea_nests.shp
2.2 .shx
The .shx file is the Shapefile geometry index.
It supports access to the records in the .shp file.
It must normally remain together with the .shp file.
2.3 .dbf
The .dbf file contains the attribute table.
For example:
1
2
3
4
nest_id
species
status
date
The attributes reported by ogrinfo come from this part of the Shapefile.
2.4 .prj
The .prj file contains the coordinate reference system definition.
For the spatial datasets inspected in this workflow, GDAL reported:
1
2
EPSG:4326
WGS 84
This is important because PostGIS needs to know the spatial reference associated with the geometry.
2.5 .sbn and .sbx
These are spatial index files used by some ESRI software.
They are not normally required in the ogr2ogr command.
For example:
1
2
baea_nests.sbn
baea_nests.sbx
can remain alongside the Shapefile, but the import command still references:
1
baea_nests.shp
3. General inspection procedure
Before importing a spatial file, we used:
1
ogrinfo -so -al filename.shp
The important information to inspect is:
1
2
3
4
5
6
Layer name
Geometry
Feature Count
Extent
Layer SRS
Data fields
For example:
1
ogrinfo -so -al baea_nests.shp
This showed:
1
2
3
Geometry: Point
Feature Count: 70
EPSG:4326
The same process should be used for every Shapefile before importing it.
For Excel files, the same command can be used:
1
ogrinfo -so -al filename.xlsx
The XLSX driver reports the sheet/layer, fields, feature count, and whether geometry exists.
4. BAEA datasets
4.1 baea_nests
Files:
1
2
3
4
5
6
baea_nests.dbf
baea_nests.prj
baea_nests.sbn
baea_nests.sbx
baea_nests.shp
baea_nests.shx
Inspection command:
1
ogrinfo -so -al baea_nests.shp
Results:
1
2
3
4
Layer: baea_nests
Geometry: Point
Feature Count: 70
CRS: EPSG:4326
Fields:
1
2
3
4
5
postgis_fi Integer64
lat_y_dd Real
long_x_dd Real
status String
nest_id Integer64
Interpretation
This is a spatial point dataset.
No geometry-type problem was identified during inspection, so the normal PostGIS import command can be used.
Import:
1
2
3
4
5
6
7
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
baea_nests.shp \
-nln miller.baea_nests \
-lco GEOMETRY_NAME=geom \
-lco FID=gid
The expected PostGIS geometry is:
1
2
Point
SRID 4326
5. BAEA surveys
File:
1
baea_surveys.xlsx
Inspection:
1
ogrinfo -so -al baea_surveys.xlsx
Results:
1
2
3
4
Layer: survey_results
Geometry: None
Feature Count: 2000
SRS: unknown
Fields:
1
2
3
4
5
id Integer
nest Integer
user String
date Date
result String
Interpretation
This is not a spatial dataset.
There is no geometry and no CRS.
Therefore it should be imported as a normal PostgreSQL table rather than as a PostGIS geometry table.
Import:
1
2
3
4
5
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
baea_surveys.xlsx \
-nln miller.baea_surveys
No geometry options are required.
6. GBH rookery dataset
Files:
1
2
3
4
5
6
gbh_rookeries.dbf
gbh_rookeries.prj
gbh_rookeries.sbn
gbh_rookeries.sbx
gbh_rookeries.shp
gbh_rookeries.shx
Initial inspection:
1
ogrinfo -so -al gbh_rookeries.shp
GDAL reported:
1
2
3
Geometry: Polygon
Feature Count: 55
CRS: EPSG:4326
Fields:
1
2
3
postgis_fi Integer64
species String
activity String
6.1 Import error
The initial import used the default geometry type detected by GDAL:
1
2
3
4
5
6
7
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
gbh_rookeries.shp \
-nln miller.gbh_rookeries \
-lco GEOMETRY_NAME=geom \
-lco FID=gid
The import failed with:
1
2
Geometry to be inserted is of type Multi Polygon,
whereas the layer geometry type is Polygon.
and:
1
2
ERROR: Geometry type (MultiPolygon)
does not match column type (Polygon)
6.2 Why this happened
The layer summary reported:
1
Polygon
but at least one actual feature was:
1
MultiPolygon
Therefore the PostGIS column created as:
1
geometry(Polygon,4326)
could not accept a MultiPolygon.
6.3 Checking geometry types
The geometry types can be investigated with SQLite SQL through GDAL:
1
2
3
ogrinfo gbh_rookeries.shp \
-dialect SQLite \
-sql "SELECT ST_GeometryType(geometry), COUNT(*) FROM gbh_rookeries GROUP BY ST_GeometryType(geometry)"
This is preferable to assuming that the layer-level geometry description represents every feature.
6.4 Corrected import
Use:
1
2
3
4
5
6
7
8
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
gbh_rookeries.shp \
-nln miller.gbh_rookeries \
-nlt MULTIPOLYGON \
-lco GEOMETRY_NAME=geom \
-lco FID=gid
The important option is:
1
-nlt MULTIPOLYGON
This makes the target geometry compatible with the actual source features.
7. Linear projects dataset
Files:
1
2
3
4
5
6
linear_projects.dbf
linear_projects.prj
linear_projects.sbn
linear_projects.sbx
linear_projects.shp
linear_projects.shx
Inspection:
1
ogrinfo -so -al linear_projects.shp
GDAL reported:
1
2
3
Geometry: Line String
Feature Count: 1109
CRS: EPSG:4326
Fields:
1
2
3
4
postgis_fi Integer64
type String
row_width Real
Project Integer64
7.1 Import error
The initial import was:
1
2
3
4
5
6
7
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
linear_projects.shp \
-nln miller.linear_projects \
-lco GEOMETRY_NAME=geom \
-lco FID=gid
It failed with:
1
2
Geometry to be inserted is of type Multi Line String,
whereas the layer geometry type is Line String.
and:
1
2
ERROR: Geometry type (MultiLineString)
does not match column type (LineString)
7.2 Cause
Again, the layer-level geometry was reported as:
1
LineString
but one or more actual features were:
1
MultiLineString
7.3 Geometry check
The geometry types can be checked using:
1
2
3
ogrinfo linear_projects.shp \
-dialect SQLite \
-sql "SELECT ST_GeometryType(geometry), COUNT(*) FROM linear_projects GROUP BY ST_GeometryType(geometry)"
7.4 Corrected import
Use:
1
2
3
4
5
6
7
8
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
linear_projects.shp \
-nln miller.linear_projects \
-nlt MULTILINESTRING \
-lco GEOMETRY_NAME=geom \
-lco FID=gid
The important option is:
1
-nlt MULTILINESTRING
8. Raptor nests
Files:
1
2
3
4
5
raptor_nests.dbf
raptor_nests.prj
raptor_nests.sbn
raptor_nests.shp
raptor_nests.shx
Inspection:
1
ogrinfo -so -al raptor_nests.shp
Results:
1
2
3
Geometry: Point
Feature Count: 876
CRS: EPSG:4326
Fields:
1
2
3
4
5
6
7
8
9
postgis_fi Integer64
lat_y_dd Real
long_x_dd Real
lastsurvey Date
recentspec String
recentstat String
Nest_ID Integer64
longitude Real
latitude Real
Import
No geometry conversion option was required.
1
2
3
4
5
6
7
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
raptor_nests.shp \
-nln miller.raptor_nests \
-lco GEOMETRY_NAME=geom \
-lco FID=gid
Expected geometry:
1
2
Point
SRID 4326
The table contains coordinate attributes such as:
1
2
latitude
longitude
but those are attributes. The actual spatial geometry comes from the Shapefile geometry and is stored in the PostGIS geom column.
9. Raptor surveys
File:
1
raptor_surveys.xlsx
Inspection:
1
ogrinfo -so -al raptor_surveys.xlsx
Results:
1
2
3
4
Layer: survey_results
Geometry: None
Feature Count: 2000
SRS: unknown
Fields:
1
2
3
4
5
id Integer
nest Integer
user String
date Date
result String
Interpretation
Like baea_surveys.xlsx, this is a non-spatial table.
Import:
1
2
3
4
5
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
raptor_surveys.xlsx \
-nln miller.raptor_surveys
No geometry options are necessary.
10. Buowl datasets
Files provided:
1
2
3
4
5
buowl_habitat.dbf
buowl_habitat.prj
buowl_habitat.shp
buowl_habitat.shx
buowl_surveys.xlsx
These files were identified as part of the dataset collection, but their ogrinfo output was not recorded during the workflow documented above.
Therefore their exact geometry type, feature count, CRS, and attribute definitions should be inspected before importing them.
10.1 Habitat Shapefile
Run:
1
ogrinfo -so -al buowl_habitat.shp
Record at least:
1
2
3
4
5
6
Layer name
Geometry
Feature Count
Extent
Layer SRS
Data fields
Then determine whether a geometry-type conversion is necessary before running ogr2ogr.
The expected import pattern is:
1
2
3
4
5
6
7
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
buowl_habitat.shp \
-nln miller.buowl_habitat \
-lco GEOMETRY_NAME=geom \
-lco FID=gid
If inspection or an import error reveals mixed geometry, use the appropriate -nlt option, for example:
1
-nlt MULTIPOLYGON
or:
1
-nlt MULTILINESTRING
depending on the actual source geometry.
10.2 Habitat survey spreadsheet
Inspect:
1
ogrinfo -so -al buowl_surveys.xlsx
If it reports:
1
Geometry: None
then import it as a regular PostgreSQL table:
1
2
3
4
5
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
buowl_surveys.xlsx \
-nln miller.buowl_surveys
The exact schema should be confirmed from ogrinfo before import.
11. Important lesson: layer geometry versus feature geometry
One of the most important findings from this workflow was that the geometry reported by:
1
ogrinfo -so -al
should not always be treated as proof that every feature has exactly that geometry type.
Two examples demonstrated this.
gbh_rookeries
Layer:
1
Polygon
Actual feature:
1
MultiPolygon
Solution:
1
-nlt MULTIPOLYGON
linear_projects
Layer:
1
LineString
Actual feature:
1
MultiLineString
Solution:
1
-nlt MULTILINESTRING
This is why checking geometry types becomes important when an apparently valid ogr2ogr import fails.
12. PostgreSQL/PostGIS import patterns
Spatial Shapefile
General form:
1
2
3
4
5
6
7
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
source.shp \
-nln miller.target_table \
-lco GEOMETRY_NAME=geom \
-lco FID=gid
Spatial Shapefile with multiple geometry types
Use:
1
-nlt MULTIPOLYGON
for polygon/multipolygon data when required.
Use:
1
-nlt MULTILINESTRING
for line/multiline data when required.
Non-spatial Excel table
General form:
1
2
3
4
5
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
source.xlsx \
-nln miller.target_table
No geometry options are required.
13. Verification after import
After loading a spatial layer, inspect it from PostgreSQL with ogrinfo:
1
2
3
ogrinfo -so \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
miller.table_name
A SQL check is also useful:
1
2
3
4
5
6
SELECT
COUNT(*) AS feature_count,
ST_SRID(geom) AS srid,
ST_GeometryType(geom) AS geometry_type
FROM miller.table_name
GROUP BY ST_SRID(geom), ST_GeometryType(geom);
For a non-spatial table:
1
2
SELECT COUNT(*)
FROM miller.table_name;
To inspect tables in psql:
1
\dt miller.*
To inspect columns:
1
\d miller.table_name
14. Final dataset classification
| Dataset | Files | Type | Geometry | CRS | Special handling |
|---|---|---|---|---|---|
baea_nests |
.shp/.shx/.dbf/.prj/.sbn/.sbx |
Shapefile | Point | EPSG:4326 | Standard import |
baea_surveys |
.xlsx |
Excel | None | Unknown | Regular PostgreSQL table |
buowl_habitat |
.shp/.shx/.dbf/.prj |
Shapefile | Not yet inspected | Not yet confirmed | Inspect before import |
buowl_surveys |
.xlsx |
Excel | Not yet inspected | Not yet confirmed | Inspect before import |
gbh_rookeries |
.shp/.shx/.dbf/.prj/.sbn/.sbx |
Shapefile | Polygon / MultiPolygon | EPSG:4326 | Use -nlt MULTIPOLYGON |
linear_projects |
.shp/.shx/.dbf/.prj/.sbn/.sbx |
Shapefile | LineString / MultiLineString | EPSG:4326 | Use -nlt MULTILINESTRING |
raptor_nests |
.shp/.shx/.dbf/.prj/.sbn |
Shapefile | Point | EPSG:4326 | Standard import |
raptor_surveys |
.xlsx |
Excel | None | Unknown | Regular PostgreSQL table |
15. Recommended workflow for the remaining files
For every unprocessed file, use this sequence:
Shapefile
1
ogrinfo -so -al filename.shp
Then:
1
2
3
4
5
6
7
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
filename.shp \
-nln miller.table_name \
-lco GEOMETRY_NAME=geom \
-lco FID=gid
If the import reports:
1
Geometry type (...) does not match column type (...)
inspect the actual geometry types before changing the import:
1
2
3
ogrinfo filename.shp \
-dialect SQLite \
-sql "SELECT ST_GeometryType(geometry), COUNT(*) FROM layer_name GROUP BY ST_GeometryType(geometry)"
Then select the appropriate -nlt option.
Excel
First inspect:
1
ogrinfo -so -al filename.xlsx
If:
1
Geometry: None
import as a normal PostgreSQL table:
1
2
3
4
5
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
filename.xlsx \
-nln miller.table_name
16. Key commands used in this exercise
Inspect a dataset:
1
ogrinfo -so -al filename.shp
Inspect Excel:
1
ogrinfo -so -al filename.xlsx
Inspect geometry types:
1
2
3
ogrinfo filename.shp \
-dialect SQLite \
-sql "SELECT ST_GeometryType(geometry), COUNT(*) FROM layer_name GROUP BY ST_GeometryType(geometry)"
Import a spatial layer:
1
2
3
4
5
6
7
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
filename.shp \
-nln miller.table_name \
-lco GEOMETRY_NAME=geom \
-lco FID=gid
Import a non-spatial table:
1
2
3
4
5
ogr2ogr \
-f PostgreSQL \
"PG:host=localhost port=5432 dbname=courses user=ivan password=postgres" \
filename.xlsx \
-nln miller.table_name
Connect to PostgreSQL:
1
psql -h localhost -U ivan -d courses
List tables:
1
\dt miller.*
Inspect a table:
1
\d miller.table_name
17. Conclusion
The main practical lessons from these datasets are:
-
A Shapefile is a group of related files. The
.shpfile is the file passed to GDAL, but.shx,.dbf, and.prjare important supporting files. -
ogrinfo -so -alshould be used before importing data to determine geometry, feature count, CRS, and fields. -
ogr2ogruses essentially the same import pattern for points, lines, and polygons. -
A layer may report
PolygonorLineStringwhile individual features can containMultiPolygonorMultiLineStringgeometries. -
Geometry mismatch errors during import should be investigated rather than ignored.
-
-nlt MULTIPOLYGONand-nlt MULTILINESTRINGcan be used when the target geometry needs to accommodate the actual source features. -
Excel files with
Geometry: Noneare imported as ordinary PostgreSQL tables, not spatial PostGIS tables. -
The final target for this exercise is the
millerschema inside thecoursesdatabase.