aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuilhem Moulin <guilhem@fripost.org>2024-06-10 19:07:19 +0200
committerGuilhem Moulin <guilhem@fripost.org>2024-06-10 22:56:45 +0200
commit1e21ccecea0b81f13cea220b69e387542a861551 (patch)
tree37d88011b7fafb1a8442000edcf49c4b385cc4f9
parentf7771422135de770c640ec581646dc71aca09c94 (diff)
Add TZFlag support (for GDAl ≥3.8).
(Commented out in config.yml for now since Bookworm has only v3.6.)
-rw-r--r--config.yml10
-rwxr-xr-xwebmap-import65
2 files changed, 63 insertions, 12 deletions
diff --git a/config.yml b/config.yml
index a28ec0b..89a32c7 100644
--- a/config.yml
+++ b/config.yml
@@ -135,7 +135,11 @@ layers:
# # * UUID (UUID string representation, only valid for String).
# SubType: UUID
#
-# # Default timezone (optional), for Time and DateTime types
+# # Feature field timezone (optional), for Time, Date and DateTime types. One of:
+# # * None (unknown timezone);
+# # * Local (local time);
+# # * UTC (alias GMT); or
+# # * [+-]HH:MM or UTC[+-]HH:MM
# #TZFlag: local
#
# # Formatting precision for this field in characters (optional, this should
@@ -237,7 +241,7 @@ layers:
#width: 36
- name: skapad
type: DateTime
- #TZFlag: TODO
+ #tz: local
- name: lanskod
type: Integer
subtype: Int16
@@ -270,7 +274,7 @@ layers:
#width: 36
- name: skapad
type: DateTime
- #TZFlag: TODO
+ #tz: local
- name: kommunkod
type: Integer
subtype: Int16
diff --git a/webmap-import b/webmap-import
index 5515dd0..2939ca1 100755
--- a/webmap-import
+++ b/webmap-import
@@ -22,6 +22,7 @@ import os
import logging
import argparse
import tempfile
+import re
from fnmatch import fnmatchcase
from pathlib import Path
@@ -282,6 +283,52 @@ def parseSubFieldType(name):
else:
raise Exception(f'Unknown field subtype "{name}"')
+# Parse timezone (XXX for GDAL ≥3.8 only)
+TZ_RE = re.compile(r'(?:UTC\b)?([\+\-]?)([0-9][0-9]):?([0-9][0-9])', flags=re.IGNORECASE)
+def parseTimeZone(tz):
+ if tz is None:
+ raise Exception('parseTimeZone(None)')
+ tz2 = tz.lower()
+ if tz2 == 'none':
+ return ogr.TZFLAG_UNKNOWN
+ elif tz2 == 'local':
+ return ogr.TZFLAG_LOCALTIME
+ elif tz2 == 'utc' or tz2 == 'gmt':
+ return ogr.TZFLAG_UTC
+
+ m = TZ_RE.fullmatch(tz)
+ if m is None:
+ raise Exception(f'Invalid timezone "{tz}"')
+ tzSign = m.group(1)
+ tzHour = int(m.group(2))
+ tzMinute = int(m.group(3))
+ if tzHour > 14 or tzMinute >= 60 or tzMinute % 15 != 0:
+ raise Exception(f'Invalid timezone "{tz}"')
+ tzFlag = tzHour*4 + int(tzMinute/15)
+ if tzSign == '-':
+ tzFlag = 100 - tzFlag
+ else:
+ tzFlag += 100
+ return tzFlag
+
+# Pretty-print timezone flag, cf.
+# ogr/ogrutils.cpp:OGRGetISO8601DateTime()
+def formatTZFlag(tzFlag):
+ if tzFlag is None:
+ raise Exception('printTimeZone(None)')
+ if tzFlag == ogr.TZFLAG_UNKNOWN:
+ return 'none'
+ elif tzFlag == ogr.TZFLAG_LOCALTIME:
+ return 'local'
+ elif tzFlag == ogr.TZFLAG_UTC:
+ return 'UTC'
+
+ tzOffset = abs(tzFlag - 100) * 15;
+ tzHour = int(tzOffset / 60);
+ tzMinute = int(tzOffset % 60);
+ tzSign = '+' if tzFlag > 100 else '-'
+ return f'{tzSign}{tzHour:02}{tzMinute:02}'
+
# Validate layer creation options and schema. The schema is modified in
# place with the parsed result.
# (We need the driver of the output dataset to determine capability on
@@ -339,9 +386,8 @@ def validateSchema(layers, drvo=None, lco_defaults=None):
fld_def2['Type'] = parseFieldType(v)
elif k2 == 'subtype':
fld_def2['SubType'] = parseSubFieldType(v)
- elif k2 == 'tzflag':
- pass # TODO
- #fld_def2['TZFlag'] = v
+ elif k2 == 'tz':
+ fld_def2['TZFlag'] = parseTimeZone(v)
elif k2 == 'width' and v is not None and isinstance(v, int):
fld_def2['Width'] = v
elif k2 == 'precision' and v is not None and isinstance(v, int):
@@ -471,10 +517,10 @@ def validateOutputLayer(lyr, srs=None, options=None):
if 'TZFlag' in fld:
v1 = defn.GetTZFlag()
- v2, n2 = fld['TZFlag']
+ v2 = fld['TZFlag']
if v1 != v2:
- logging.warning('Field "%s" has TZFlag=%d, expected %d (%s)',
- fldName, v1, v2, n2)
+ logging.warning('Field "%s" has TZFlag=%d (%s), expected %d (%s)',
+ fldName, v1, formatTZFlag(v1), v2, formatTZFlag(v2))
ok = False
if 'Precision' in fld:
@@ -589,8 +635,9 @@ def createOutputLayer(ds, layername, srs=None, options=None):
defn.SetSubType(v)
if 'TZFlag' in fld:
- v, n = fld['TZFlag']
- logging.debug('Set TZFlag=%d (%s) on output field "%s"', v, n, fldName)
+ v = fld['TZFlag']
+ logging.debug('Set TZFlag=%d (%s) on output field "%s"',
+ v, formatTZFlag(v), fldName)
defn.SetTZFlag(v)
if 'Precision' in fld:
@@ -674,7 +721,7 @@ def clearLayer(ds, lyr):
return
layername_esc = escapeIdentifier(lyr.GetName())
- # GDAL <3.9 doesn't have lyr.GetDataset() so we pass the DS along with the layer
+ # XXX GDAL <3.9 doesn't have lyr.GetDataset() so we pass the DS along with the layer
drv = ds.GetDriver()
if drv.ShortName == 'PostgreSQL':
# https://www.postgresql.org/docs/15/sql-truncate.html