GeoDms 20.19.3
Pre-releasePre-release. GeoDms 20.19.3 covers everything since 20.16.0 — 20.17.0 was published, 20.18.0 through 20.19.2 followed. New in this release: a Release-build fault now leaves a minidump instead of a vanished window, a map view of an irregularly tiled grid no longer terminates the GUI when the cursor leaves the tiles, iif and its family keep the geometry composition of their arguments, and an .mmd store written below a DisableStorage case parameter is refused at write time rather than failing in the reader. Carried over from the 20.19.x line: three deadlocks that used to park a computation at 0% CPU now report themselves, connect accepts its two geometries the other way round and gains matrix variants, and the teardown and allocation fixes. The .g (GLOBIO) flavour ships alongside .m, .c and .l.
| Flavour | Asset |
|---|---|
.m — Windows, MSBuild |
GeoDms20.19.3.m-Setup-x64.exe |
.c — Windows, CMake |
GeoDms20.19.3.c-Setup-x64.exe |
.g — Windows, GLOBIO compatibility |
GeoDms20.19.3.g-Setup-x64.exe |
.l — Linux, Ubuntu 24.04 |
GeoDms20.19.3.l-linux-x64.deb, .tar.gz (+ .sha256, .sha256.p7s) |
Allocation
- A land use type that is no option no longer fails the allocation. Measuring the distance from optimum — which only builds the
DiscrAlloc completed …status line — added the shadow price to an undefined suitability, where the bidding loop itself skips such a type before doing any arithmetic. On a model with null suitabilities that ended the whole run withnumeric overflow in the shadow price arithmetic. The same addition used to wrap silently, so a type that was never an option compared as a better bid: the "at most N from optimum due to M better options" figures have been wrong for such models for as long as they have existed, and are now right. - The Simulation-of-Simplicity perturbation range is checked once per run instead of at every use, and reported before the solve starts rather than partway through an allocation.
discrete_allocarithmetic audited (#1196). The claim aggregates were summed inUInt32: two land use types at the "no limit" maximum claim sum to exactly 2^32, read back as zero, and the run was rejected as infeasible — four cells are enough, no grid needed. Shadow prices now go through checked arithmetic naming the component that overflowed, and sixdiscrete_alloc_*_pi64names offer a 64-bit perturbation.
Robustness
- Three deadlocks no longer park silently (#1233). Each showed the same thing to the user: a computation at 0% CPU, no error, no log line. The one that bit hardest is in deterministic tile accumulation, where every data tile waits for the result tile counter to reach its own number — so a tile that unwinds (a read error, a cancellation, a failed allocation) advances nothing and every later tile waits for it forever, taking the
parallel_tileloopand itsJoinwith it. Failure is now published and the waiters are woken. The lock inventory and the ceilings that make such orderings checkable are documented alongside (#1227, #1233), and every function that acquires a lock now declares the outermost acquire it makes — 207 declarations across the engine, checked on entry against what the caller holds. That made the remaining orderings checkable rather than argued, and turned up and fixed four more silent parks on the way (P5, P11, P13, P14). - A Release-build fault leaves a minidump (#1241). A structured exception used to leave nothing to debug: the handler ran only after Windows had unwound every frame between the fault and the
__try, so there was no stack to capture, and the feedback child it spawned aborted before showing its message. The net effect was a window that vanished with no dump and no text. The dump is now written in the exception filter, while the faulting frames are still on the stack, by a parked writer thread on a stack of its own — which is what makes a stack overflow dumpable at all. One filter covers the GUI main thread,GeoDmsRunand the GDAL callbacks alike. - A map view of an irregularly tiled grid no longer terminates the GUI (#1242). Moving the mouse over a part of the viewport that no tile covers ended
GeoDmsGuiQtwith no message and no dump: the tooltip handed an out-of-range location to an index routine whose precondition the caller was meant to shield, and the tile lookup then walked off the end of the tile vector. Such a location now answers an undefined row, which a tiling that need not cover its own range makes a normal outcome. GDALAllRegisteris serialized (#1234).GeoDmsRundied with0xC0000374(heap corruption) or0xC0000005, with no[E]line and part of the tif set already written, when tengdalwrite.griditems with an aggregating IntegrityCheck were pulled by oneExplicitSupplierstrigger. The fallback registration for an extension with no known driver was the one path not taken under the GDAL section lock, andGDALAllRegisteris not thread-safe.- A derived values unit stays alive (#1237). The single-argument
area/arc_length/mls_lengthderives its result unit from the geometry's coordinate unit, but a data item holds its values unit only weakly, so that parentless unit was freed while still referenced. - A process no longer aborts at exit with a corrupted heap. Reporting the final allocator summary allocates, and allocating can post to the main-thread operation queue — which, by the time a static destructor of the same library runs, has itself been destroyed, so the post freed an already-freed buffer. The damage surfaced much later as an abort during process exit, with a stack naming an unrelated allocation. It cost roughly one run in ten on a loaded machine and nothing at all on an idle one; on Linux it aborted the GUI, on Windows the heap may absorb it silently. The summary now comes from an explicit terminate step while the process is still whole.
- #1191 — what aborts the process is named, and the cancellation paths no longer abort it. #1206 — GDAL cleanup ordering at process shutdown. #1225 — a GUI closed with work abandoned no longer ends in
uncaught DmsException: Cannot find Domain unit .. - #1226 / #1227 — the export dialog no longer hangs on a cold item:
GetName()hands out a lock on the token registry, and holding it across a walk that parses an expression made the main thread wait for itself. That self-deadlock is now reported rather than parked on.
Polygons
- A ring's role is read relative to the first ring, not to the compass (#1212). The GEOS and CGAL readers judged each ring absolutely — shell iff clockwise — so a feature whose rings are uniformly counter-clockwise, which is what a source listing coordinates in latitude/longitude order gives you, lost its shell and promoted the lake behind it. Nothing was rejected and nothing was empty: ordinary-looking geometry with the wrong area. A configuration's areas may change on upgrade; the older numbers were the wrong ones. What that means in practice is measured on a real model in #1230: in the RSopen regression, 105,051 BAG building footprints stored counter-clockwise — none of which has a hole, so nothing was left to promote — were read as empty and are now read correctly, while 232 CBS land-use polygons whose rings are only partly flipped were read 2,298 ha too large and now match their stored area. Both directions occur, so an area can grow or shrink; a feature that is only partly flipped still needs
fix_winding_order. - Nesting-based winding-order operators (#302) on GEOS:
fix_winding_order,fix_polygon,has_correct_winding,geos_polygons_by_nesting. #1219 —points2polygonno longer requires a closed ring the readers close themselves. #917 — Minkowski sums take the kernel as an argument, deprecating 48bp_*names.
Storages and export
- A shapefile is written with closed rings. Both writers stored a polygon's rings exactly as the value held them, but a GeoDMS polygon need not repeat a ring's first point at the end and the ESRI specification requires that it does. The result violated the format, and
gdal.vect— which a.shpStorageNameselects since 17.0.0, so the engine's own default reader for the file it just wrote — refuses such a file with "Non closed ring detected". gdalwrite.vectrecognises a geometry by its composition, not by the namegeometry(#1232). A geometry column called anything else was measured against the driver's creation field types — and no OGR field type corresponds tofpoint,dpointorspoint— so the write was refused with "driver ESRI Shapefile does not support writing of values", and the layer was created without a geometry field.- A layer's companion columns come into play at write time (#1229).
- Writing vector layers (#711).
gdalwrite.vectcreated a field for every storable attribute and left the rest<null>when only one column was of interest. A column of interest now pulls in the other columns of its layer for exactly as long as the outside interest lasts. A unit that both declares aSpatialReferenceand has a calculation rule no longer loses that CRS, so a shapefile gets its.prjagain. - Missing VAT when exporting to dbf or gpkg (#973) — four separate defects, among them a native shapefile branch that compared against the wrong driver name and had been dead since 2023, with the resulting storage failure reported to the user as "Export ready".
- A unit carrying an attribute of another domain exports as a table again (#1145).
.mmd dictionaries
DisableStoragebelow the holder is refused at write time (#1245). A template's case parameter declaredDisableStorage = "True"and instantiated below an.mmdstorage holder was written into the dictionary as a stored domain of its own, with the attributes beside it declared against that unit instead of against the store. The store looked healthy and failed in the reader, with "Domain mismatch between the specified Domain (/store) and the domain of the results (/store/meta/domain)". Two causes:TreeItem::Copyreturned early for a case parameter before carrying the configured properties over, so theDisableStoragenever reached the instantiated parameter; and the writer accepted it where it did. The property now carries, and the writer refuses the shape rather than producing a store that cannot be read.- A zero-length attribute file reads (#1246) instead of failing the store that contains it.
- Unit paths are absolute, in declarations and checks alike (#1195). A domain configured as
../RegioUnitwent into the dictionary verbatim, so a reader merging that store elsewhere resolved the dots against its structure:Unknown identifier '../RegioUnit'. The store could then not be read at all. - A differing dictionary root name is provenance, not a warning (#1194), and a check that cannot be built now names itself (#1197).
IntegrityChecks and fences
- A check now guards an item read from a storage (#1209) — such an item has no calculation rule, so consumers were handed an unguarded source reference and the check ran only after data preparation.
- A check on an
ExplicitSuppliersitem guards the item that declares it (#1218). Selecting either item in the TreeView previously ran no check at all. - A phase runs when a member is reached (#1167), collecting a phase member is re-entrant (#1201), indirect expressions behind a fence are rejected (#1199), and the phase scan honours the static argument policy (#1224).
Charts and views
- A new chart no longer waits for its own classification (#1221), position axes fit their data instead of forcing zero into view (#1222), row-number axes are labelled from the domain's
Labelattribute (#1207) and tick labels take the thousand separator (#1223). - Each chart kind has its own icon (#1211), the View and Window menus and window titles are drawn from the TreeView's icon set (#1220), and tree icons say what an item is (#319) rather than which views can be opened on it.
- The active TreeView item is visible again (#1235). Selecting an item changed nothing on screen — no colour, no focus caret, not even the 2px border the delegate believed it was drawing, because the base style re-ran
initStyleOptionon its own copy and painted the background back over it. - Every DataView caption leads with the kind of view, and a missing
SpatialReferenceis named (#1238). A map view whose world coordinate unit declares neither aSpatialReferencenor aLabelproduced a caption starting with a bare", Active layer: ...", which read as a formatting glitch rather than as the fact that no coordinate system was configured. - Editing a calculated class break, colour or label copies the values first (#634); a classification can be copied from one map view and applied in another (#734); the EventLog filter panel is no longer squeezed (#1213); the legend-width menu items name the keys that actually work (#1217). The main window's placement is recorded while it is up, so a session ending in a crash no longer restores a placement from days ago.
Networks
connectaccepts its two geometries the other way round (#1228).connect,connect_eq,connect_ne,connect_info(_eq/_ne)anddist_infotook the arcs first and gave every point its nearest arc. Passing the points first turns them into a choice set: every arc gets the nearest of them, measured to the nearest location on that arc. A reversedconnect_infonames its relation memberpoint_relrather thanarc_rel, and it is the key of the arc whose null value waives aneq/necomparison — the mirror of the forward rule. The filtered search now also reports when it found nothing, and the reversed search is capped atmaxSqrDistinstead of being cancelled.connect_matrix/dist_matrix(#1236) — whereconnect_infogives each feature its nearest point, these give every point within a maximum distance, as a sparse matrix: a fresh domain of (feature, point) pairs carryingarc_rel,point_relanddist, plusCutPoint/InArc/InSegm/SegmIDfor theconnect_matrixhalf. Both have_eqand_nevariants.- Bi-criteria (pareto)
impedance_matrix(#856) — a second Dijkstra engine keeping the pareto-optimal set over two impedances instead of collapsing them into one weighted cost. od:StartPoint_relis written (#1210) — the member was created, allocated and committed but never filled, so requesting it yielded uninitialised memory. NYI since 2022.
Configuration language and diagnostics
iif,union,union_dataandlookupkeep the geometry composition of their arguments (#1240).cond ? poly_a : poly_bproduced anarcwhatever its arguments were, because a ternary took the composition registered for the operator rather than of its values — and with the composition check of #1038 an attribute declared(poly)was then reported as deprecated, with the wrong composition propagating to its readers so that the configuration could not work around it.union,union_dataandlookuphad the mirror defect for multipoint. One rule now serves all four: the first value argument seeds the composition, arguments that agree keep it, and arguments that genuinely disagree — part ring, part polyline — are an error, since no composition describes such a set and the composition is what a storage writes as its geometry type.- The composition deprecation names the calculation rule that produced it, and its advice covers multipoint (#1243). The message named the item carrying the differing declaration but not what computed the composition — and after #1240 the deciding operator can be several steps away. It also recommended
points2sequenceandpoints2polygononly, two answers both wrong for a multipoint; the advice now namespoints2multi_pointas well, in both places it is given, and says GeoDms 21 rather than "a future major version" for when the warning becomes an error. - The engine no longer reports case mix-ups about its own names (#1161). The regression battery emitted 2747 case warnings over 233 configurations with not one clean; ten of every eleven were the engine disagreeing with itself.
unit,value,item,param,attrandnrofrowsare now canonically lower case, chosen by census over 1146.dmsfiles. Battery: 2747 → 40 lines. union_datawarns when its arguments do not match the parts of its result domain (#990), the legacy brace range spelling is caught on either bound (#1165), andselect_spec/collect_spec(#337) take their four choices as a;-separated word list.table_specand stablesort_indexhelpers (#421); editor navigation into a template instantiation (#471); a worker thread's internal error names the item it was working on (#1202); a configuration can decide whether its hidden items are shown (#694).- Source Description covers what a storage feeds (#975), and the CalcCache machinery is retired (#1189) — a configuration still spelling
%calcCacheDir%now gets an unknown-placeholder error.
Packaging and build
- The
.g(GLOBIO) flavour, built from the same solution with a committedGeoDmsGlobioswitch against the older GDAL/GEOS stack, with its own output tree and vcpkg manifest. Install it only where that stack is required;.mremains the ordinary Windows build. - The VS Code extension ships with the setup, and the editor language definitions are generated from the operator registry: 23 names that no longer resolved to anything registered were removed.
- The
.gflavour is gated like the others (#1231) —batch\TestGlobioReleaseUnit.batandTestGlobioDebugUnit.batrun the unit suite, the testcases battery and the shipped-content test againstbin_GLOBIO\, and the setup script installs only when all three pass. - A GUI test script no longer aborts on a byte outside ASCII, reports a script error as exit 1, and dispatches
SaveValueInfoon Linux too (#1239). - Multi-version Python bindings (#1105), and #1186 — the
.csetup now ships the MSVC runtime instead of borrowing whatever redistributable happened to be inSystem32. - Every text blob in the repository is stored with LF. Source only; nothing about the shipped binaries changes.