CRS Creator Internals#
This page documents the internals of WISER’s CRS Creator (the “Reference System Creator”) — the spatial tool that lets a user define a custom coordinate reference system (CRS) by hand, without an EPSG / authority code. It is a companion to the Georeferencer Internals page; custom CRSs created here become selectable as the output CRS when georeferencing.
Overview#
The dialog is a QTabWidget with two ways to define a CRS, both of which end at the
same ApplicationState.add_user_created_crs(name, srs, state) store:
From Parameters — a user describes a CRS in geodetic terms (a datum shape, a prime meridian, and a map projection with its parameters) and the tool assembles those into a PROJ4 string and converts it to an
osr.SpatialReference.From CRS String — a user pastes a ready-made CRS definition (WKT, PROJ, or an authority code such as
EPSG:4326) and the tool tries to load it, reporting whether WISER can build it and, if so, letting the user name and store it.
From then on either result behaves like any other CRS in WISER.
flowchart LR
subgraph Params["From Parameters tab"]
U["User fills dialog fields"] --> P["_create_crs():<br/>build PROJ4 string"]
P --> Y["pyproj.CRS.from_proj4 → WKT"]
Y --> O["osr.SpatialReference<br/>(ImportFromWkt)"]
end
subgraph Str["From CRS String tab"]
US["User pastes WKT / PROJ / EPSG:code"] --> V["_build_srs_from_string():<br/>osr.SetFromUserInput"]
V --> O2["osr.SpatialReference<br/>(valid?)"]
end
O --> A["ApplicationState.add_user_created_crs(name, srs, state)"]
O2 --> A
A --> G["Georeferencer output-CRS chooser<br/>(as UserGeneratedCRS)"]
Core files:
File |
Responsibility |
|---|---|
|
|
|
Qt Designer UI ( |
|
|
The dialog is launched lazily from App.show_reference_creator_dialog
(src/wiser/gui/app.py).
Class & State#
classDiagram
class QDialog["QDialog (Qt)"]
class ReferenceCreatorDialog {
reference_creator_dialog.py
+_create_crs()
+_export_creator_state()
-_new_crs : osr.SpatialReference
}
class CrsCreatorState {
<<immutable snapshot>>
+proj_type
+shape_type
+semi_major_value
+axis_ingest_type / value
+lon_meridian
+center_lon / latitude
+latitude_choice
+polar_stereo_scale / sign
+source_wkt
+is_string_origin
}
QDialog <|-- ReferenceCreatorDialog
ReferenceCreatorDialog --> CrsCreatorState : exports / reloads
ReferenceCreatorDialog owns the UI and the build logic. A family of _init_*
methods wire each field group with validators (e.g. latitude clamped to ±90, longitude
to ±180, the CRS name restricted to alphanumeric + underscore).
CrsCreatorState is an immutable snapshot of every field. After a successful build
it is stored alongside the resulting osr.SpatialReference, which is what lets a saved
custom CRS be re-selected and have its parameters reloaded into the dialog for editing.
Its source_wkt field (and the is_string_origin convenience property) marks a CRS
that was built from a pasted string on the From CRS String tab rather than from the
parameter fields — see Validating a pasted CRS string.
The dialog’s behavior is driven by several enums:
Enum |
Values |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
UI / Workflow#
The dialog is a QTabWidget (tabWidget) with two pages, and a single shared
OK/Cancel QDialogButtonBox beneath the tabs. Which builder the OK button runs
depends on the active tab: accept() calls _create_crs() for the parameter tab and
_on_add_crs_string() for the string tab (and keeps the dialog open if the string tab
has nothing valid to add).
From Parameters tab#
The form is organized into the groups a CRS definition needs:
Datum shape —
SPHEROID(a single radius) orELLIPSOID(semi-major axis plus either the semi-minor axis or the inverse flattening, selected viaEllipsoidAxisType).Prime meridian — the longitude offset applied to the datum (
+pm).Projection — one of
EQUI_CYLINDRICAL,POLAR_STEREO, orNO_PROJECTION.Projection parameters — central meridian (
center_lon), a latitude value, and aLatitudeTypeschoice of whether that latitude is the central latitude or the latitude of true scale. Polar Stereographic additionally needs either a scale factor (central-latitude mode) or a pole sign (true-scale mode).Name — the key the CRS is stored and displayed under.
A reset button clears the fields (both tabs); the create/accept button runs
_create_crs().
From CRS String tab#
A QPlainTextEdit (pedit_crs_string) to paste the definition into, a name field
(ledit_string_crs_name), a Validate button, an Add to WISER button (disabled
until a validation succeeds), and a read-only result box (pedit_crs_string_result)
that shows a green success summary or a red error. See below.
Validating a pasted CRS string#
The From CRS String tab lets a user check whether WISER can load an arbitrary CRS definition without hand-entering every parameter.
_build_srs_from_string(text) is the core helper. It builds an osr.SpatialReference
and calls SetFromUserInput(text) — the broadest GDAL entry point, which accepts WKT
(WKT1 and WKT2), PROJ strings, and authority codes like EPSG:4326. It returns
(srs, None) on success or (None, error) on failure, catching both the exception and
non-zero return-code styles GDAL uses. On success the axis mapping is forced to
OAMS_TRADITIONAL_GIS_ORDER, the same convention the parameter path and the
georeferencer apply.
Validate (
_on_validate_crs_string) parses the text and shows either a green summary (name, geographic/projected, authority:code if present) or the red error, and enables Add to WISER only on success. Editing the text afterwards (_on_crs_string_text_changed) invalidates the prior result and re-disables Add.Add to WISER (
_on_add_crs_string) requires a name and a validated CRS (it re-validates if needed), then stores it viaadd_user_created_crswith aCrsCreatorStatewhosesource_wktis set to the CRS’s WKT.
Because a string-origin CRS has no parameter fields to reload, selecting one in the
Starting Ref System combo is special-cased in _on_starting_crs_changed: instead of
driving the parameter widgets it switches to the string tab, repopulates
pedit_crs_string with the stored source_wkt, and re-validates.
CRS Construction (_create_crs)#
_create_crs() validates the fields, builds a PROJ4 string, and converts it to an OSR
spatial reference.
The PROJ4 fragments assembled per choice:
Choice |
PROJ4 fragment |
|---|---|
Sphere |
|
Ellipsoid |
|
Base (always) |
|
No Projection |
|
Equidistance Cylindrical, central lat |
|
Equidistance Cylindrical, true-scale lat |
|
Polar Stereographic, central lat |
|
Polar Stereographic, true-scale lat |
|
The conversion goes PROJ4 → pyproj.CRS → WKT → osr.SpatialReference
(ImportFromWkt), and the axis mapping is forced to OAMS_TRADITIONAL_GIS_ORDER — the
same convention the georeferencer applies — so coordinate order is consistent everywhere
the CRS is used.
Integration#
add_user_created_crs(name, crs, state) stores the pair in ApplicationState:
self._user_created_crs: Dict[str, Tuple[osr.SpatialReference, CrsCreatorState]] = {}
(keyed by name, with a confirmation prompt before overwriting an existing name). The
Georeferencer reads this map in _update_output_srs_cbox_items and wraps each entry in a
UserGeneratedCRS so it appears in the output-CRS chooser. The store is identical
whether the CRS came from the parameter form or from a pasted string, so a
string-imported CRS is usable as an output CRS just like any other. The stored
CrsCreatorState is what lets the CRS Creator reload a previously created CRS back into
its fields (parameter tab) or its pasted text (string tab, via source_wkt) for
editing. See Georeferencer Internals for how the resulting
CRS feeds the warp.