Skip to content

Instantly share code, notes, and snippets.

@stelf
Created June 2, 2025 16:28
Show Gist options
  • Save stelf/03cbea1ce7de2fb248f6abb49dc252a9 to your computer and use it in GitHub Desktop.
Save stelf/03cbea1ce7de2fb248f6abb49dc252a9 to your computer and use it in GitHub Desktop.
feed data using the best ogr that supports pg. windows11 version onyl.
# PowerShell script: Find ogr2ogr.exe, check PostgreSQL support, build DSN from .env, and import shapefile
# Enhanced version with proper handling for unnamed geometry columns
param(
[string]$inputFile = "data\cadaster_all.shp",
[string]$table = $([System.IO.Path]::GetFileNameWithoutExtension($inputFile)), # Infer from input file
[ValidateSet("auto", "sql", "select", "geometry")]
[string]$geometryMode = "auto",
[switch]$overwrite,
[string[]]$selectedColumns = @("*"), # Default to all columns
[string]$sourceLayer # Will be inferred if not provided
)
function Get-SourceLayerName {
param(
[string]$filePath,
[string]$ogrinfoPath
)
try {
# Use -so (summary only) to get just the layer name, capture all output
# Ensure the command is executed and output is captured correctly
$ogrinfoOutput = & $ogrinfoPath $filePath -so 2>&1
# Debugging: Print raw output
Write-Host -ForegroundColor DarkYellow "Raw ogrinfo output for layer detection:"
$ogrinfoOutput | ForEach-Object { Write-Host -ForegroundColor DarkYellow $_ }
# Iterate through each line of output to find the layer name
foreach ($line in $ogrinfoOutput) {
if ($line -match '^1: (.+) \(.*\)$') {
return $Matches[1].Trim()
}
}
} catch {
Write-Warning "Could not determine source layer name for '$filePath': $($_.Exception.Message)"
}
return $null
}
function Get-EnvFile {
$dir = Get-Location
while ($null -ne $dir) {
$envPath = Join-Path $dir ".env"
if (Test-Path $envPath) { return $envPath }
$parent = Split-Path $dir -Parent
if ($parent -eq $dir) { break }
$dir = $parent
}
return $null
}
function Get-DSNVars {
param($envFile)
$vars = @{
PGUSER = $null
PGPASSWORD = $null
PGHOST = $null
PGDATABASE = $null
PGPORT = "5432"
}
Get-Content $envFile | ForEach-Object {
if ($_ -match '^\s*PGUSER\s*=\s*(.+)$') { $vars.PGUSER = $Matches[1].Trim() }
elseif ($_ -match '^\s*PGPASSWORD\s*=\s*(.+)$') { $vars.PGPASSWORD = $Matches[1].Trim() }
elseif ($_ -match '^\s*PGHOST\s*=\s*(.+)$') { $vars.PGHOST = $Matches[1].Trim() }
elseif ($_ -match '^\s*PGDATABASE\s*=\s*(.+)$') { $vars.PGDATABASE = $Matches[1].Trim() }
elseif ($_ -match '^\s*PGPORT\s*=\s*(.+)$') { $vars.PGPORT = $Matches[1].Trim() }
}
return $vars
}
# Find OGR executables with PostgreSQL support
$ogr2ogrPg = $null
$ogrinfoPg = $null
# Try to use cached values first
if ($env:OGR2OGR_PG_PATH -and (Test-Path $env:OGR2OGR_PG_PATH)) {
$ogr2ogrPg = $env:OGR2OGR_PG_PATH
}
if ($env:OGRINFO_PG_PATH -and (Test-Path $env:OGRINFO_PG_PATH)) {
$ogrinfoPg = $env:OGRINFO_PG_PATH
}
if (-not $ogr2ogrPg -or -not $ogrinfoPg) {
Write-Host -ForegroundColor Magenta "Looking for ogr2ogr.exe and ogrinfo.exe with proper PostgreSQL driver support"
$ogrExecutables = @("ogr2ogr.exe", "ogrinfo.exe")
$foundPaths = @{}
foreach ($exe in $ogrExecutables) {
$paths = @()
if (Get-Command fd -ErrorAction SilentlyContinue) {
$paths = fd $exe -tx / | Where-Object { $_ -match "$exe$" }
} else {
$paths = Get-ChildItem -Path C:\ -Filter $exe -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName
}
if ($paths) {
Write-Host -ForegroundColor Magenta "Found few '$exe' instances, lets verify driver"
$pgSupportedPath = $paths | Where-Object {
(& $_ --formats 2>&1) -match 'PostgreSQL -vector-'
} | Select-Object -First 1
if ($pgSupportedPath) {
$foundPaths[$exe] = $pgSupportedPath
Write-Host -ForegroundColor Green "Found PostgreSQL-enabled '$exe': $pgSupportedPath"
} else {
Write-Warning "No PostgreSQL-enabled '$exe' found among discovered paths."
}
} else {
Write-Warning "No '$exe' found on the system."
}
}
$ogr2ogrPg = $foundPaths["ogr2ogr.exe"]
$ogrinfoPg = $foundPaths["ogrinfo.exe"]
if ($ogr2ogrPg) {
$env:OGR2OGR_PG_PATH = $ogr2ogrPg # Cache for future runs
}
if ($ogrinfoPg) {
$env:OGRINFO_PG_PATH = $ogrinfoPg # Cache for future runs
}
}
if (-not $ogr2ogrPg) {
Write-Error "No ogr2ogr.exe with PostgreSQL support found."
exit 2
}
if (-not $ogrinfoPg) {
Write-Error "No ogrinfo.exe with PostgreSQL support found."
exit 6 # New exit code for ogrinfo missing
}
# Infer source layer name if not provided
if (-not $sourceLayer) {
$sourceLayer = Get-SourceLayerName -filePath $inputFile -ogrinfoPath $ogrinfoPg
if (-not $sourceLayer) {
Write-Error "Could not determine source layer name. Please specify it using -sourceLayer parameter."
exit 5
}
}
# Build DSN from .env
$envFile = Get-EnvFile
if (-not $envFile) {
Write-Error ".env file not found."
exit 3
}
$pgVars = Get-DSNVars $envFile
if (-not $pgVars.PGUSER -or -not $pgVars.PGHOST -or -not $pgVars.PGDATABASE) {
Write-Error "Missing PGUSER, PGPASSWORD, PGHOST, or PGDATABASE in .env."
exit 4
}
$dsn = "postgresql://$($pgVars.PGUSER):$($pgVars.PGPASSWORD)@$($pgVars.PGHOST):$($pgVars.PGPORT)/$($pgVars.PGDATABASE)"
# Summarize
Write-Host -ForegroundColor Cyan "Preparing to run ogr2ogr with the following parameters:"
$configSummary = [PSCustomObject]@{
"Input File" = $inputFile
"Target Table" = $table
"Source Layer" = $sourceLayer
"Geometry Mode" = $geometryMode
"Overwrite" = $overwrite
"Selected Columns" = ($selectedColumns -join ", ")
"PostgreSQL DSN" = "postgresql://$($pgVars.PGUSER):<password_hidden>@$($pgVars.PGHOST):$($pgVars.PGPORT)/$($pgVars.PGDATABASE)"
}
$configSummary | Format-Table -AutoSize
# Build common ogr2ogr parameters
$commonParams = @(
"-progress"
"-f", "PostgreSQL"
"PG:$dsn"
$inputFile
"-nln", $table
"-lco", "GEOMETRY_NAME=geom"
"-lco", "FID=id"
"-nlt", "PROMOTE_TO_MULTI" # Ensure correct geometry type handling
)
# Add overwrite flag if specified
if ($overwrite) {
$commonParams += "-overwrite"
Write-Host -ForegroundColor Yellow "Table will be overwritten if it exists"
}
# Execute the ogr2ogr command based on geometry mode
switch ($geometryMode) {
"auto" {
Write-Host -ForegroundColor Yellow "Using auto mode - let ogr2ogr handle geometry and all attributes automatically"
Write-Host $ogr2ogrPg @commonParams $sourceLayer
&$ogr2ogrPg @commonParams $sourceLayer
}
"sql" {
Write-Host -ForegroundColor Yellow "Using SQL mode - explicitly selecting specified fields with geometry"
if ($selectedColumns -contains "*") {
&$ogr2ogrPg @commonParams -sql "SELECT * FROM `"$sourceLayer`"" $sourceLayer
} else {
$columnList = $selectedColumns -join ", "
&$ogr2ogrPg @commonParams -sql "SELECT $columnList FROM `"$sourceLayer`"" $sourceLayer
}
}
"select" {
Write-Host -ForegroundColor Yellow "Using select mode - selecting specified fields only"
if ($selectedColumns -contains "*") {
# -select with * is not standard, so we'll just use auto behavior
Write-Host -ForegroundColor Yellow "Warning: '-select *' is not directly supported. Using auto mode behavior for attribute selection."
&$ogr2ogrPg @commonParams $sourceLayer
} else {
$columnList = $selectedColumns -join ","
&$ogr2ogrPg @commonParams -select $columnList $sourceLayer
}
}
"geometry" {
Write-Host -ForegroundColor Yellow "Using geometry mode - for completely unnamed geometry columns, importing all attributes"
&$ogr2ogrPg @commonParams -sql "SELECT *, GEOMETRY FROM `"$sourceLayer`"" $sourceLayer
}
}
Write-Host -ForegroundColor Green "ogr2ogr command completed."
# Additional notes for handling unnamed geometry columns:
<#
HANDLING UNNAMED GEOMETRY COLUMNS IN OGR2OGR:
1. AUTO MODE (-geometryMode auto):
- Uses -select parameter to let ogr2ogr auto-detect geometry
- Works when geometry column exists but may not have a proper name
- Recommended for most standard shapefiles
2. SQL MODE (-geometryMode sql):
- Uses -sql parameter with explicit SELECT statement
- Geometry is automatically included when using SQL
- Good for complex field selection scenarios
3. SELECT MODE (-geometryMode select):
- Uses -select parameter with specific field names
- Geometry is handled automatically by ogr2ogr
- Similar to auto but more explicit about field selection
4. GEOMETRY MODE (-geometryMode geometry):
- Uses -sql with explicit GEOMETRY reference
- For cases where geometry column is completely unnamed
- Uses "SELECT *, GEOMETRY FROM layer_name" syntax
COMMON ISSUES AND SOLUTIONS:
- If geometry column has no name: Use -geometryMode geometry
- If you get "geometry column not found": Try -geometryMode auto first
- For complex queries: Use -geometryMode sql with custom SQL
- For simple field selection: Use -geometryMode select
ADDITIONAL OGR2OGR OPTIONS FOR GEOMETRY HANDLING:
- -lco GEOMETRY_NAME=geom: Sets the target geometry column name
- -nlt PROMOTE_TO_MULTI: Promotes geometries to multi-type (handles mixed geometry types)
- -t_srs EPSG:4326: Transform to specific coordinate system if needed
- -s_srs EPSG:xxxx: Specify source coordinate system if not detected
- -dim XY: Force 2D geometry (removes Z coordinates)
PROJ DATABASE ERROR:
If you encounter "proj_identify: ... DATABASE.LAYOUT.VERSION.MINOR = 2 whereas a number >= 4 is expected",
it means you have multiple PROJ installations or an outdated one.
You might need to set the PROJ_LIB environment variable to point to the correct PROJ database:
$env:PROJ_LIB = "C:\Path\To\Your\Preferred\PROJ\share"
USAGE EXAMPLES:
.\ogr.feed.data.ps1 -geometryMode auto
.\ogr.feed.data.ps1 -geometryMode sql -inputFile "data\other.shp" -table "other_table"
.\ogr.feed.data.ps1 -geometryMode geometry # For completely unnamed geometry columns
#>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment