Repository navigation
MapSmith 0.9.0
Records still declare spec_version 1.0.0-draft.10, and no key is renamed.
Three keys of MapSmith's own are new, each declared with the reason it is not
a synonym of anything the specification has: x-mapsmith:areas_measured_on in
crs_decisions, from apportion_by_area, and x-mapsmith:ballpark_share and
x-mapsmith:outside_area_of_use inside crs_decisions.transformation. Three
check names are new, all from apportion_by_area:
x-mapsmith:extensive_total_preserved, x-mapsmith:every_source_value_placed
and x-mapsmith:no_zone_gives_more_than_it_holds. What
reproject_layer writes changes in two cases, both described under Fixed:
coordinates outside every published operation's area are no longer moved by
an operation from elsewhere, and a geographic CRS counted from another
meridian whose operations are published only for its Greenwich twin
(EPSG:4806, Monte Mario from Rome) goes through that twin. Shapely 2.1 and
pyarrow 23.0.1 or later are required.
Added
apportion_by_area: move counts and rates from the zones they are reported
in to zones that cross them. Cases by municipality to health districts,
census-tract population to school catchments. Each column is declared
extensive (a count, split by share of area) or intensive (a rate, averaged by
area); a call declaring neither is refused, because splitting a rate or
averaging a count gives a plausible wrong number. Each count comes with
_minand_maxthat hold however it is distributed inside the source
zones; rate bounds rest on the same uniformity the estimate assumes, and the
record says which is which. Zones are cut in the source zones' own CRS,
where a boundary both layers share is the same line, and a zone counts as
wholly inside a target by a topological test, not by a ratio of areas.
Shares are ratios of areas measured on an equal-area surface -- the source
CRS when it is one, otherwise a plane centred on the zones on their own
datum, named in the record -- so the pieces of each zone add up to it.
Whether targets overlap is decided zone by zone, so a few square metres of
overlapping boundaries are said in the record where they put a zone's value
in two targets, and a check fails if a zone with no overlap gives out more
than it holds. Invalid zones are repaired and the repair recorded; a zone
the repair leaves with no area or with a coordinate that is not a number,
zones spread over more than a hemisphere, layers writing longitude in two
conventions, and a target that the source CRS would tear at one of its
seams (the 180th meridian, a pole: caught by area and by position, whatever
the units) are refused; targets keep their own CRS; and the counts written
are checked against the pieces they came from.
Fixed
crs_decisions.transformationnow describes what PROJ did to the data's
own coordinates. Which operation PROJ selects depends on where a
coordinate is, and the record asked at one point chosen from the source CRS
alone -- for EPSG:4326, the middle of the world, in the Gulf of Guinea. It
was wrong in both directions: WGS 84 data in Europe moved to EPSG:3035, or in
England to EPSG:27700, was recorded as a ballpark (is_ballpark: true) where
PROJ applied a published 1 m or 2 m shift; and NAD27 coordinates in Italy were
recorded as a 7 m shift where PROJ applied none -- the error a ballpark record
exists to report, hidden by it. Every operation that moves an input now hands
over the moved layer, PROJ is asked what it did at a spread of the data's own
points, inside and outside the areas of use of the operations it has -- outside
them it applies no shift for some pairs (NAD27) and stretches a published
operation for others (DHDN, the Swiss grid), so the answer is PROJ's and not
inferred. A layer straddling the edge of a datum grid is recorded as a
ballpark withx-mapsmith:ballpark_sharesaying how many of its vertices got
no shift;x-mapsmith:outside_area_of_usesays how many lie outside the area
of every operation published for the pair, whatever PROJ then did with them;
a move of no coordinates at all records that nothing was transformed; and a
check with no data to hand asks where the two CRSs' areas of use overlap.reproject_layerno longer applies an operation outside its area of use,
and its output changes where it did. When PROJ's own choice at its probe
point was a ballpark it substituted the first published operation for the
pair, wherever the data was: WGS 84 data in Madrid moved into EPSG:3003 by
the Italian operation came out 84.5 m from PROJ's own result, and WGS 84
data in Italy moved into EPSG:27700 by the British one, 248 m. Now it
substitutes only an operation whose area of use contains every vertex, and
otherwise leaves each point where PROJ does. A geographic CRS counted from
another meridian whose operations are published only for its Greenwich twin
goes through that twin -- EPSG:4806 (Monte Mario, Rome) and EPSG:4819 (Nord
Sahara 1959, Paris) among the 23 tried; NTF Paris, EPSG:4807, has an
operation of its own and does not change. A station in Piedmont on EPSG:4806
now lands on the published 4 m operation's answer, where it was 6.3 m off.better_available_mnames only an operation that covers the data and is
not installed. It reported any published operation for the pair: EPSG:4806
data in Piedmont was told a 44 m operation needed its grid installed, when
that operation needs none and is valid in Sardinia only.- A record written on a failing path no longer claims an earlier run's
output. With a dataset already at the output path, a run refused by its
preconditions, or one that crashed before writing, recorded the digest of
the earlier bytes as its own output -- and a lineage walk by digest then
attributed them to the inputs of the run that failed -- and its record
replaced theirs, leaving them with none. Such a record now omitsoutput
and says why, and where a record on disk still describes the bytes at the
path it is left in place and the failing run's record is written beside it
as<output>.failed.provenance.json, which the error names. Bytes the
failing run did write keep their digest, including when a check of the
output crashes after the write.
Changed
- Shapely 2.1 or later is required (was 2.0):
apportion_by_areauses its
maximum inscribed circle to check that a reprojected target still holds its
own interior. - pyarrow 23.0.1 or later is required (was 21), the release fixing
CVE-2026-25087, a use-after-free reading Arrow IPC files; MapSmith reads none,
and the floor keeps an install at the minimum from carrying it. pytest 9.0.3
or later for the test extra.