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#

A user describes a CRS in geodetic terms — a datum shape (sphere or ellipsoid), a prime meridian, and a map projection with its parameters — and the tool assembles those into a PROJ4 string, converts it to an osr.SpatialReference, and stores it by name in ApplicationState. From then on it behaves like any other CRS in WISER.

        flowchart LR
    U["User fills dialog fields"] --> S["ReferenceCreatorDialog<br/>(validated state)"]
    S --> P["_create_crs():<br/>build PROJ4 string"]
    P --> Y["pyproj.CRS.from_proj4 → WKT"]
    Y --> O["osr.SpatialReference<br/>(ImportFromWkt)"]
    O --> A["ApplicationState.add_user_created_crs(name, srs, state)"]
    A --> G["Georeferencer output-CRS chooser<br/>(as UserGeneratedCRS)"]
    

Core files:

File

Responsibility

src/wiser/gui/reference_creator_dialog.py

ReferenceCreatorDialog, CrsCreatorState, the PROJ4 builder, enums

src/wiser/gui/ui_files/reference_system_creator.uigenerated/reference_system_creator_ui.py

Qt Designer UI (Ui_ReferenceSystemCreator)

src/wiser/gui/app_state.py

add_user_created_crs / get_user_created_crs store

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
    }
    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.

The dialog’s behavior is driven by several enums:

Enum

Values

ProjectionTypes

EQUI_CYLINDRICAL, POLAR_STEREO, NO_PROJECTION

ShapeTypes

ELLIPSOID, SPHEROID

EllipsoidAxisType

SEMI_MINOR, INVERSE_FLATTENING

LatitudeTypes

CENTRAL_LATITUDE, TRUE_SCALE_LATITUDE

Sign

POSITIVE (+), NEGATIVE (-) — polar-stereo pole

Units

METERS, DEGREES


UI / Workflow#

The form is organized into the groups a CRS definition needs:

  • Datum shapeSPHEROID (a single radius) or ELLIPSOID (semi-major axis plus either the semi-minor axis or the inverse flattening, selected via EllipsoidAxisType).

  • Prime meridian — the longitude offset applied to the datum (+pm).

  • Projection — one of EQUI_CYLINDRICAL, POLAR_STEREO, or NO_PROJECTION.

  • Projection parameters — central meridian (center_lon), a latitude value, and a LatitudeTypes choice 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; the create/accept button runs _create_crs().


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

+R=<a>

Ellipsoid

+a=<a> +rf=<inv_f> (inverse flattening derived from semi-minor if needed)

Base (always)

+pm=<lon_meridian> +no_defs

No Projection

+proj=longlat

Equidistance Cylindrical, central lat

+proj=eqc +lon_0=<center_lon> +lat_0=<lat>

Equidistance Cylindrical, true-scale lat

+proj=eqc +lon_0=<center_lon> +lat_ts=<lat>

Polar Stereographic, central lat

+proj=stere +lat_0=<lat> +lon_0=<center_lon> +k=<scale> +x_0=0 +y_0=0

Polar Stereographic, true-scale lat

+proj=stere +lon_0=<center_lon> +lat_0=<±>90 +lat_ts=<lat> +x_0=0 +y_0=0

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 stored CrsCreatorState is what lets the CRS Creator reload a previously created CRS back into its fields for editing. See Georeferencer Internals for how the resulting CRS feeds the warp.