Skip to content

Releases: bravemaster3/GeoDeploy

v1.6.4 — a redirect to http is not https

Choose a tag to compare

@bravemaster3 bravemaster3 released this 11 Sep 18:01
e8cba25
  • A tile source that redirects to http is now recognised as insecure, and moved to the host
    that does serve https.
    v1.6.3's mixed-content guard followed redirects and asked only for "a
    200 with bytes in it", which the reported Google satellite URL passes while ending on plain
    http
    : http://www.google.cn/maps/vt?… answers 302 → http://www.google.com/maps/vt?…
    whichever scheme you ask it in. So the guard called the https form secure and would have stored
    an address that still cannot draw — the browser blocks a chain that ends insecure exactly as it
    blocks an http URL typed in directly. Every hop of the chain must now be https. And because
    the redirect changed only the host, the source is stored at
    https://www.google.com/maps/vt?…, which serves byte-identical tiles. A redirect that changes
    the path or the query is not followed: a consent wall or a per-tile CDN URL is not a
    template, and rewriting a source to one would store an address that works for tile 0/0/0 and
    nothing else.

Upgrading

docker compose build geodeploy-api && docker compose up -d --force-recreate geodeploy-api celery,
or Update now in Settings. No plugin or CLI change.

A source already stored with an http:// address is not rewritten — the resolution happens
when a source is added. Delete it and add it again.

If you would rather not wait for a rebuild, the resolved address works today on any version: paste
the https form of it directly.

v1.6.3 — a hatched polygon comes home hatched

Choose a tag to compare

@bravemaster3 bravemaster3 released this 11 Sep 17:34
7589805
  • A hatched polygon comes home hatched. Opening a portal from GeoDeploy drew every pattern
    fill — hatch, cross, dense, line, point — as a solid block of colour, on every route at once
    (fast preview, editable, with a token and without), because every route ends in the same symbol
    builder. Two faults, both on the way back. A solid fill was painted under the tile in the same
    colour as the hatch drawn on top of it, which is a solid block by definition: measured at 82%
    transparent in QGIS and 0% after the trip home. And the tile's width was stated in millimetres
    where QgsRasterFillSymbolLayer counts in pixels — the one size in the plugin whose default
    unit is not millimetres — so a 16-pixel hatch drew at 4 and its diagonals collapsed into a grey
    stipple that reads as a flat wash.
  • A plain fill beneath a pattern is carried in the tile. MapLibre draws fill-pattern
    instead of fill-color, so a red polygon with a black hatch published with the red missing.
    The fill underneath is now painted into the tile itself, which is the only place it can live —
    and is what lets the browser and QGIS draw the same thing. A polygon that is only a pattern
    says it has no outline, rather than being given the default blue one.
  • Eleven pattern kinds are now pinned by rendering them and comparing the ink, in both QGIS
    images. The style dictionary looked correct throughout this bug, which is why the check is a
    measurement of pixels rather than of keys.

Upgrading

This one is the QGIS plugin, not the instance. Nothing on the server changed — no rebuild, no
re-publish needed.

Install plugin 0.7.2
and restart QGIS. Patterned layers already on the instance will open hatched straight away; push a
layer again if you want the portal to gain the fill colour that now travels inside the tile.

v1.6.2 — a colour's own transparency travels

Choose a tag to compare

@bravemaster3 bravemaster3 released this 11 Sep 16:14
2ab7417
  • A colour's own transparency now travels. QGIS has two opacities and only one of them was
    being read: "Layer rendering ▸ Opacity" dims the whole symbol, while the alpha slider inside the
    colour picker dims just that colour. QColor.name() returns #rrggbb and drops the second, so a
    polygon whose fill was set to 0% in the colour dialog published as a solid block. The portal card
    disagreed with the map, which was a fair clue: the card is a screenshot of the QGIS canvas, so it
    still had the alpha the style had lost. The two multiply in QGIS — a 50% symbol holding a 50%
    colour draws at 25% — and they now multiply into the one number GeoDeploy carries, for fills,
    lines and markers
    alike. A fully transparent outline travels as no outline.
  • line_opacity, the twin of marker_opacity, so a translucent boundary has somewhere to
    live. Drawn by the portal and the editor preview, round-tripped by the plugin, and settable from
    the CLI (--line-opacity).
  • An http:// tile source is resolved when it is added, not left to fail in the browser. A
    browser refuses to load http:// tiles into an https:// page — the source registers, the layer
    appears in the list, and the map stays empty with the error only in the visitor's console.
    Reported as two Google tile URLs added side by side, one drawing and one not, while both worked
    in QGIS (a desktop application, with no such rule). An http address is now upgraded when the
    provider answers over https — almost all do, including the reported one — and refused with the
    reason when it cannot. Only on an instance served over https: on a plain-http install an http
    tile is exactly right.

Upgrading

Three parts, and they are independent:

  • The instance — docker compose build geodeploy-api geodeploy-ui && docker compose up -d --force-recreate geodeploy-api celery geodeploy-ui, or Update now in Settings. Re-publish a portal for the new line opacity to reach it.
  • The QGIS plugin — install 0.7.1 and restart QGIS. This is the half that reads the colour's alpha, so a layer has to be pushed again for the transparency to arrive.
  • The CLI — pip install --upgrade geodeploy (1.6.1).

An external source already stored with an http:// address is not rewritten. Delete it and add it again; it will be upgraded to https on the way in.

v1.6.1 — "no basemap" actually removes the basemap

Choose a tag to compare

@bravemaster3 bravemaster3 released this 11 Sep 12:08
977b1c3

A one-fix release on top of v1.6.

"No basemap" actually removes the basemap

Choosing it in the portal editor appeared to do nothing — and the editor was innocent.

The editor's preview is a real published bundle in an iframe, which is what makes it faithful.
So what the preview shows is decided at publish, in the bundle builder. __none__ is deliberately
not in the basemap catalog — there is no service behind it; it is the absence of one — so the
lookup found nothing, the repoint step never ran, and the template's own basemap stayed baked in.

The runtime switcher could still turn the basemap off afterwards, which is exactly why it seemed
that only the published portal honoured the setting. It didn't: the stored choice was being
ignored in both places, and the portal only looked right because you had switched it off by hand.

The basemap is now taken out of the style at publish rather than hidden on load, on a white
ground — with no painted ground, what shows through a map is the canvas, which is black. No flash
of a map nobody asked for, and the switcher opens with None ticked.

Only the template's sources are removed: a data source that happens to be called basemap is the
map itself. Everything else is unchanged — a real basemap still repoints, a portal that chose
nothing looks exactly as it did, and an unknown id (a typo, or a basemap since removed from the
catalog) leaves the template's basemap rather than silently publishing a blank map.

Upgrading

docker compose build geodeploy-api && docker compose up -d --force-recreate geodeploy-api celery,
or Update now in Settings. Existing published portals keep their current basemap until they are
re-published.

QGIS plugin 0.7.2 — a hatched polygon comes home hatched

Choose a tag to compare

@bravemaster3 bravemaster3 released this 11 Sep 17:34
7589805

Install the zip below — Plugins ▸ Manage and Install Plugins ▸ Install from ZIP — and restart
QGIS. The instance needs nothing; this is a plugin-only release.

A hatched polygon comes home hatched

Opening a portal from GeoDeploy drew every pattern fill — hatch, cross, dense, line, point — as a
solid block of colour, on all three routes at once (fast preview, editable, with a token and
without), because all three end in the same symbol builder.

Two faults, and neither was visible in the style: fill_pattern was present, correct and
byte-identical across the whole round trip. Only the pixels were wrong.

A solid fill was painted under the tile, in the style's own colour, with the hatch then laid on
top of it in that same colour — a solid block by definition. A diagonal hatch QGIS drew 82%
transparent came back 0% transparent.

And the tile's width was stated in millimetres, where QgsRasterFillSymbolLayer counts in
pixels. It is the one size in this plugin whose default unit is not millimetres, so a 16-pixel
hatch tile drew at 4 pixels — finer than the hatch's own period, which turns the diagonals into a
grey stipple that reads as a flat wash rather than as a small hatch.

A fill beneath a pattern now travels in the tile

MapLibre draws fill-pattern instead of fill-color, so a plain fill sitting under a hatch has
nowhere else to live: a red polygon with a black hatch used to publish with the red missing. It is
painted into the tile itself now, which is what lets the browser and QGIS draw the same thing. And
a polygon that is only a pattern says it has no outline, instead of being given the default blue
one.

Checked by rendering, not by reading

Eleven pattern kinds are drawn before and after and their ink compared — the mean alpha of the
rendered swatch — since a style-dict assertion could not see any of this. Run against the code
before this change, the new section fails eleven times with "QGIS drew 15%, it came back 100%".

569 matrix and 349 real-QGIS checks pass on both qgis:ltr and qgis:4.2.

QGIS plugin 0.7.1 — a colour's own transparency travels

Choose a tag to compare

@bravemaster3 bravemaster3 released this 11 Sep 16:15
2ab7417

Install the zip below — Plugins ▸ Manage and Install Plugins ▸ Install from ZIP — and restart
QGIS. Needs GeoDeploy v1.6.2 on the
instance for the line half.

A colour's own transparency travels

QGIS has two opacities and only one was being read:

  • Layer rendering ▸ Opacity dims the whole symbol — this one always travelled;
  • the alpha slider inside the colour picker dims just that colour — this one was dropped,
    because QColor.name() returns #rrggbb.

So a polygon whose fill was set fully transparent in the colour dialog published as a solid block.
The portal card showed it correctly, which was the clue: the card is a screenshot of your QGIS
canvas, so it still had the alpha the style had lost.

The two multiply in QGIS — a half-opacity symbol holding a half-opacity colour draws at a quarter
— and they now multiply into the one number GeoDeploy carries. For fills, lines and markers
alike, both directions, and a fully transparent stroke travels as no outline rather than a solid
border in whatever hue the invisible one happened to be.

Pinned by 12 new round-trip checks covering a polygon, a line and a marker, including the
multiplication and a second trip with no drift.

Re-push to see it

The plugin is the half that reads the alpha, so a layer already on the instance keeps the styling
it was pushed with until you push it again.

geodeploy 1.6.1 (CLI and Python client)

Choose a tag to compare

@bravemaster3 bravemaster3 released this 11 Sep 16:15
2ab7417

pip install --upgrade geodeploy

--line-opacity

A line's own opacity, the twin of the existing --marker-opacity. --opacity is the layer's;
QGIS keeps a symbol's separately — and an alpha inside the colour picker as well — and they
multiply when the map is drawn.

geodeploy layers style boundaries --color '#1f4fd8' --line-width 2 --line-opacity 0.4

It exists because a translucent boundary had nowhere to live: the line paint knew only the layer's
opacity, so a boundary drawn at 40 per cent in QGIS published fully opaque. Carried through the
portal, the editor preview and the QGIS plugin as line_opacity, and treated as a per-class shape
key — a class that differs only by transparency becomes its own render layer.

Also in this version: the vendored client that the QGIS plugin ships is rebuilt from it, so plugin
0.7.1 and this release agree about the style vocabulary.

v1.6 — every symbol QGIS can draw

Choose a tag to compare

@bravemaster3 bravemaster3 released this 10 Sep 12:13
8c6f493

The QGIS round trip, closed

Styling has travelled between QGIS and GeoDeploy since v1.4, for the symbology GeoDeploy itself
has. This release is about everything else QGIS draws — and about being honest where a web map
cannot follow. All of it is measured against real PyQGIS on both QGIS 3.44 LTR and QGIS 4.2, in
CI, on every commit: 349 regression checks and 531 more generated from QGIS's own symbol and
renderer registries.

  • Rule-based rendering. A rule tree becomes one render layer per rule, each with its filter,
    its symbol and its own scale range, and comes back as a rule tree. This is the one most real QGIS
    projects reach for, and it used to arrive as a single flat symbol.
  • A symbol is everything it stacks. A casing under a road, a dashed overlay on a solid line, a
    hatch: QGIS builds these by stacking simple lines in one symbol, and only the first was read. A
    rule's symbol stacks the same way — that half was missing for longer, so a red line under blue
    dashes published as plain red inside a rule while publishing correctly outside one.
  • Markers along a line, with their spacing — arrows on a river, ticks on a boundary, circles on
    a route. The spacing used to travel as a number without its unit, so 20 screen pixels came back
    as 15 millimetres and the markers landed three times too far apart, which reads as "they are
    missing".
  • Hatch and pattern fills, gradients, line offsets, 2.5D, centroid fills, heatmaps, arrow lines,
    blend modes
    — each either travels or is reported as approximated.
  • A class carries its own symbol, not only its colour. A categorized layer whose classes differ
    by dash, width or marker shape is drawn that way on both sides.
  • Anything GeoDeploy has no words for travels as a picture. An SVG marker, a font marker, a
    hatch, a multi-layer symbol — QGIS renders it, and the portal draws the thing you drew. A style
    embedded in a file only names a path to an SVG on the machine that wrote it, which is of no use
    anywhere else. Sizes survive the trip: QGIS sizes a raster marker by its canvas, not its ink.
  • Labels are symbology too. Label rule trees (water blue at 9pt, a town brown at 11), the text
    expression, halo, letter spacing, capitalisation, offset, priority, per-label scale ranges, and
    where the text sits — at a point, or bent along the line, on it or above it.

Labels drawn once

  • A big polygon no longer carries its name in a grid across itself. MapLibre places a symbol per
    geometry as the tile delivers it, and tiles clip — so a polygon crossing four tiles was four
    geometries, each getting the label. No style property can say otherwise. Labels for polygons are
    now drawn from a point source (ST_PointOnSurface, so the label is inside the shape — a
    centroid falls in the hole of a ring and puts a country's name in the sea).
  • "Label every part of a multi-part feature" is an option now, off by default, as it is in QGIS.
    An archipelago wants it; a country with two islands does not.

External sources — every kind a web map can draw

xyz | wms | wfs becomes xyz | wms | wmts | wfs | ogcapi | vectortile | pmtiles, from the web
UI, the CLI and the QGIS plugin alike.

  • OGC API - Features: paste the landing page, the collection, or the items URL — all three work.
  • Third-party vector tiles: a .pbf template or its TileJSON.
  • PMTiles: a remote archive read a tile at a time by your instance, so the whole file is never
    downloaded, the portal needs no PMTiles library, and the provider needs no CORS policy.
  • WMTS as its own kind, rather than being sent as a WMS to a server that only speaks GetTile.
  • Everything that can be validated is validated when you add it — a WFS and an OGC API
    collection are fetched, a TileJSON is read, a PMTiles header is parsed. That is where a wrong
    layer name or an unreachable host surfaces, and where the layer inside a tile set, its zoom range
    and its extent come from.
  • What is read as data is proxied same-origin. A raster tile is fetched the way the web has
    fetched images for twenty years; GeoJSON, a vector tile and a PMTiles byte range are cross-origin
    requests, and a provider without Access-Control-Allow-Origin breaks those silently — an empty
    layer and a console error a visitor never sees.
  • WCS is deliberately not a kind, and says so: GetCoverage returns a coverage, not map images,
    so there is nothing a web map can draw from it. Most WCS servers publish the same data over WMS.

Without a token, the plugin now sees what it should

  • A published public portal opens the same with or without an account — same colours, rules,
    labels and folders. There used to be two implementations of "what does this portal look like":
    the authored styling for a signed-in user, and a backwards translation of the published
    MapLibre paint for everyone else. The reverse is lossy by construction, so every symbology fix
    landed on one side and the anonymous path drifted further behind with each release.
    GET /api/public/portals/{slug} serves the authored configuration to anybody — the same map
    style.json already publishes as paint, in the vocabulary it was written in.
  • A public layer added on its own arrives styled. The anonymous index is deliberately the
    smallest view of an instance and carries no default_style; nothing had ever asked for the
    detail that does.

In the browser

  • Delete several layers at once. Checkboxes in My Data, a select-all per section over what the
    filter shows, and one Delete. The confirmation lists every item by name and the union of the
    portals they appear in; deletions run one at a time (each prunes portals and re-publishes them),
    and one failure is skipped with its reason rather than abandoning the rest.
  • "No basemap" is a choice everywhere a map is drawn, on a white ground rather than a black one.
  • Changing the basemap changes the basemap and nothing else. It used to be able to hide one of
    the portal's own layers.
  • Zoom to layer works on a phone. Five call sites padded the fit by the sidebar's full width;
    on a phone that panel is 85vw, so the padding exceeded the canvas and MapLibre silently declined
    to move — a button that did nothing, with nothing in any log. Touch targets grew from 22px to
    34px on coarse pointers, and a folder's zoom no longer hides behind hover.
  • A control flyout fits the screen it opens on. The basemap list ran off the bottom of a
    landscape window with eight entries, and "None" could not be reached.

Uploads and ingest

  • Every layer of a multi-layer GeoPackage is ingested, including one with no styling saved and
    one whose name collides after truncation.
  • A zipped GeoPackage, FileGDB or shapefile set is unpacked and read.

Fixes worth naming

  • A raster no longer draws unstretched (usually blank) when its style could not be read; the
    portal keeps what it had rather than having it replaced with nothing.
  • A rate-limited upload waits and retries instead of failing a group push half way, so pushing a
    portal no longer has to be done twice.
  • A stale geodeploy module no longer stops the QGIS plugin connecting after an in-place upgrade.

For operators

Nothing to do. The new tile function and the four new external_sources columns are applied on
start, and Martin's configuration is rebuilt with them. Published portals are unchanged until you
re-publish; a portal re-published on this version gains the label-point source for its polygons.

QGIS plugin 0.7.0 — every symbol QGIS can draw

Choose a tag to compare

@bravemaster3 bravemaster3 released this 10 Sep 12:14
8c6f493

The QGIS plugin as shipped with GeoDeploy v1.6. Install it from the zip below —
Plugins ▸ Manage and Install Plugins ▸ Install from ZIP — and restart QGIS.

Requires QGIS 3.28 or newer, and runs on QGIS 4 (Qt6). Both ends of that range are tested
against real PyQGIS in CI on every commit.

Every symbol QGIS can draw

Styling has travelled between QGIS and GeoDeploy since 0.3. This release is about everything else
QGIS draws — and about being honest where a web map cannot follow.

  • Rule-based rendering, both ways: a rule's filter, its symbol and its own scale range.
  • A symbol is everything it stacks — a casing under a road, a dashed overlay on a solid line —
    including inside a rule, which was the half that lasted longest: a red line under blue dashes
    published as plain red in a rule while publishing correctly outside one.
  • Markers along a line, with their spacing. The spacing used to travel as a number without its
    unit, so 20 screen pixels came back as 15 millimetres and the markers landed three times too far
    apart — which reads as "they are missing".
  • Hatch and pattern fills, gradients, line offsets, 2.5D, centroid fills, heatmaps, arrow lines,
    blend modes.
  • A class carries its own symbol, not only its colour.
  • Anything GeoDeploy has no words for travels as a picture — an SVG or font marker rendered by
    QGIS, at the size QGIS drew it. QGIS sizes a raster marker by its canvas, not its ink, which is
    why sizes used to shrink a little on every trip.
  • Labels as symbology: rule trees, the text expression, halo, letter spacing, capitalisation,
    offset, priority, per-label scale ranges, and where the text sits — at a point or bent along the
    line, on it or above it — plus "label every part of a multi-part feature".

External services, both directions

A WMS, WMTS, XYZ, WFS, OGC API - Features, vector tile set or PMTiles archive open in QGIS is
now registered as a GeoDeploy external source instead of being refused. Nothing is uploaded, the
provider keeps serving it, and their attribution travels with it.

Inside a group this also fixes something worse than a missing feature: a remote layer used to fall
in with the local files, and the first export aborted the whole publish — so one basemap in a group
meant nothing reached the portal.

What has no home is refused by name, with the way forward: an ArcGIS REST service, and a WCS
(which is not a display service at all — GetCoverage returns a coverage, not map images).

A portal's own external sources open and push back correctly too. They used to be read as the kind
they hold, so a WMS source opened as "raster layer N" and pushing the group back silently
repointed the portal at whatever layer N happened to be.

Without a token, a portal opens exactly as it does with one

There were two implementations of "what does this portal look like": with a token the plugin read
the authored styling; without one it read the published style.json and translated the MapLibre
paint backwards. That reverse is lossy by construction — a rule tree, a stacked stroke, a
per-class marker and a label's placement have no paint value to recover them from — so every fix
landed on one side and the anonymous path drifted further behind with each release.

One path now. An instance too old for the public portal route still opens, from the published
style, and says so in the log.

A public layer added on its own arrives styled, too.

The fast preview

Renamed from "As the portal draws it", which was a promise a different rendering engine cannot
keep. It is a preview of the published map: fast, close, and not a facsimile.

Three things it got wrong are fixed: a rule ladder now thins out with zoom instead of drawing every
rung at once, labels appear at the zoom the portal shows them rather than one level early, and a
line's labels are drawn at all (a line labelled at a point draws nothing in QGIS, silently).

Also

  • supportsQt6 is declared. Without it, QGIS 4's plugin manager will not enable the plugin —
    an invisible failure, with nothing wrong in any log.
  • A stale geodeploy module no longer stops the plugin connecting after an in-place upgrade.
  • A rate-limited upload waits and retries instead of failing a group push half way, so pushing a
    portal no longer has to be done twice.
  • The push dialog names what will be uploaded, registered, restyled and left out — before anything
    happens.

Not yet

  • A QML carried alongside the friendly style, so a symbol no web map can draw (a shapeburst
    fill, a geometry generator) survives QGIS ⇄ QGIS unchanged while the portal draws its
    approximation.
  • Inverted polygons.
  • 3D from a fast preview. QGIS has no 3D renderer for vector tiles in either 3.44 or 4.2, so a
    group says so and points at the editable source rather than drawing it flat in silence. Opened
    from its data, a layer's 3D is real and editable.

geodeploy 1.6.0 (CLI and Python client)

Choose a tag to compare

@bravemaster3 bravemaster3 released this 10 Sep 12:15
8c6f493

pip install --upgrade geodeploy

The Python client and command line for GeoDeploy, as shipped with v1.6. It is also the client
the QGIS plugin vendors, so the two never disagree about what an instance can do.

External sources: three kinds became seven

geodeploy sources add --type now takes xyz, wms, wmts, wfs, ogcapi, vectortile and
pmtiles.

geodeploy sources add "Topo"      https://example.org/wmts --type wmts --layer-name topo
geodeploy sources add "Roads"     https://example.org/ogc/collections/roads --type ogcapi
geodeploy sources add "Basemap"   https://tiles.example.org/tiles.json --type vectortile
geodeploy sources add "Buildings" https://files.example.org/b.pmtiles --type pmtiles

The client offered three while the instance held seven, so --type pmtiles was refused before the
request was made
— which reads as "GeoDeploy does not support PMTiles" when it does.

  • --source-layer names the layer inside a vector tile set or PMTiles archive. The instance
    reads it from a TileJSON or a PMTiles header where the service publishes one; give it where the
    service does not, because a style that names no layer draws nothing at all, silently.
  • --matrix-set for a WMTS (default GoogleMapsCompatible).
  • An OGC API collection may be in the URL (…/collections/roads) or in --layer-name; one of the
    two is required, and that is checked before anything is sent.
  • WCS is deliberately absent and says so: GetCoverage returns a coverage, not map images, so
    there is nothing a web map can draw from it. Most WCS servers publish the same data over WMS.

Everything that needs the network is validated by the instance when the source is registered — a
WFS and an OGC API collection are fetched, a TileJSON is read, a PMTiles header is parsed — so a
wrong layer name or an unreachable host fails then, rather than as an empty layer on a published
map.

Two more label properties

  • --label-line-position {on,above,below} — a contour's height is written on its line; a
    river's name usually sits above it.
  • --label-per-part — label every part of a multi-part feature instead of the feature once. A flag,
    so its absence never turns the setting off on a style that had it on.

Compatibility

No breaking changes. Python 3.9 and newer, as before — the floor is what QGIS 3.28 LTR ships, since
the plugin vendors this package.