ThinkGeo.com    |     Documentation    |     Premium Support

V15 beta160 follow-up: remaining GPU style parity, source lifecycle, WFS V2 and WMS gaps

Thanks for the beta155–160 fixes. We have updated our integration and removed the workarounds covered by those changes.

Our application combines shapefiles, database feature layers and rasters. Users frequently edit symbols, thematic styles and labels. We retain classic sources for queries, selection and printing, and
use reusable PMTiles archives for eligible shapefiles.

The following items remain, based on inspection of our beta160 application assemblies and your replies. We have separated concrete SDK/API gaps from cases that still need a Windows visual
reproduction.

1. WFS fixes appear to be missing from the V2 classes

The geometry/namespace fixes and new GeometryType property appear in WfsAsyncLayer / WfsAsyncFeatureSource. Our application uses the separate WfsV2AsyncLayer / WfsV2AsyncFeatureSource.

In the inspected V2 implementation:

  • Geometry detection still checks for the literal gml prefix.
  • Requests do not automatically supply the feature-type NAMESPACES binding.
  • Neither V2 class exposes GeometryType.

We therefore still remove PROPERTYNAME from GetFeature requests, add the namespace binding from capabilities, and detect geometry type ourselves.

Request: apply these fixes to the V2 classes too, including inherited geometry properties, and expose GeometryType after OpenAsync using the schema already obtained.

2. WMS scale metadata is exposed, but still needs capabilities parsing

WmsServerLayer.MinScale / MaxScale are available, but the inspected implementation leaves several integration gaps:

  • Legacy ScaleHint values are stored without conversion to scale denominators.
  • Scale limits are not inherited from parent layers.
  • The cloning used by GetServerLayers() omits child layers.
  • Unspecified limits need to remain distinguishable from explicit values.

We still parse capabilities to calculate effective ranges for selected layers and groups.

Request: expose effective ranges with documented units, preserve the layer hierarchy, and represent unspecified limits explicitly.

We understand your decision not to enforce advertised ranges automatically. An opt-in request filter would be useful, but unconditional enforcement is not required.

3. Text masks still lack full classic parity

Filled rectangular masks now translate. The remaining cases are outlined masks, rounded/circular masks and masks without fill. These still lose fidelity and warn, causing our classic fallback.

Request: preserve mask borders and shape while fitting the mask to text, through nine-slice rendering or an equivalent approach.

4. Two point-style cases still require our own sprite baking

First, the circle-paint path cannot preserve point masks and patterned ink. We still rasterize those styles ourselves.

Second, nested point styles appear to be drawn into the parent sprite and then emitted separately by the translator. That duplicates ink and can change opacity.

Requests:

  • Automatically use a faithful sprite path when a circle paint cannot represent the style.
  • Ensure nested point components are drawn exactly once.

The general sprite-sizing fix works for us; these are separate remaining cases.

5. Hatch, gradient and texture strokes remain unsupported

The warning/exception fixes are useful. However:

  • Hatch strokes still become the hatch background colour, or disappear when that background is transparent.
  • Gradient and texture pens now warn instead of throwing, but do not translate.
  • Unsupported gradient/texture area fills also remain a limitation.

Request: actual rendering support for these brushes, particularly hatch strokes. Consistent warnings resolve the diagnostic problem, but not cartographic parity.

6. Rich dynamic highlights still require tiled snapshots

Ordinary highlights use InMemoryGeometrySource. Richer styles still require FeatureLayerTranslator and tiled snapshots.

There is also a smaller API gap: TrackPoint.SizeInPixels requires an explicit size, while StyleImages does not expose the dimensions produced by its improved sprite measurement.

Requests:

  • A richer dynamic geometry styling API for frequently changing highlights.
  • Access to measured sprite dimensions/bounds and pixel ratio, or support for using a sprite’s intrinsic dimensions in dynamic points.

7. Live source changes work, but cache retention and raster replacement remain limited

General vector-source-count changes now work. We retain the host and unchanged readers for those transitions.

The remaining issues are:

  • Vector cache retention is positional. Removing a source from the middle can invalidate later slots even when those sources are unchanged.
  • Restyling to zero vector sources still rejects the rebind.
  • Raster subscriptions/textures are retained by source ID, so we currently replace the host when raster bindings change.

Requests:

  • Reconcile retained vector sources by stable identity, with explicit invalidation when their data source changes.
  • Support transitions to a raster-only or empty scene.
  • Provide a supported raster-source replacement/removal path that updates subscriptions and textures while retaining unaffected sources.
  • Document source ownership and disposal during successful, rejected and cancelled rebinds.

We understand that changing the tile grid is a separate operation and may require resetting the scene.

Finally, the per-draw TileCache snapshot addresses the original split-pass issue. Our draw gate remains because we also need to drain writers before retiring cache directories or closing sources. An
SDK drain/rotation operation would simplify this, but we consider the original cache-snapshot defect fixed.

Our highest priorities are WFS V2 parity, nested-point duplication, complete text masks, hatch strokes, and source replacement/cache retention.

Thanks Ben :smiley:

Hi Julian,

All of it is now in 15.0.0-beta162.

1. WFS V2 - WfsV2AsyncFeatureSource / WfsV2FeatureSource and their layers now behave like the 1.x classes:

  • geometry found through the schema’s namespace bindings, including one declared in a base type
  • NAMESPACES=xmlns(prefix,uri) sent automatically with a prefixed TYPENAMES
  • GeometryType available after OpenAsync (Invalid before)

You can drop the PROPERTYNAME workaround, the namespace binding and your own geometry detection.

2. WMS

  • GetServerLayers() returns the hierarchy: Clone keeps ChildLayers
  • a child without its own range reports its parent’s
  • 1.1.1 ScaleHint is converted, so MinScale / MaxScale are scale denominators whichever version answered
  • HasMinScale / HasMaxScale tell “no limit” from a declared 0

3. Text masks - drawn in nine slices. Outlined, rounded-corner, rounded-end and outline-only masks now match the classic renderer, no warning. Sprite JSON stretchX / stretchY / content is honoured too.

Still warned: MaskType.Circle draws with rounded ends; a hatch or gradient mask fill stretches with the label.

4. Points

  • nested CustomPointStyles are drawn once
  • a circle with a mask, hatch, gradient or texture takes the sprite path and keeps all of it

5. Hatch and texture

  • hatch pens stroke with their pattern, with or without a background colour; texture pens tile their image; texture fills are fill-patterns; dashes and caps apply on top
  • classic side: new GeoPen(brush, width) on a LineStyle no longer throws, and the canvas strokes with the brush - classic and GPU now match

Still warned: gradient pens and gradient fills.

6. Sprite size

  • StyleImages.TryGetSize(id, out width, out height, out pixelRatio)
  • a TrackPoint with SizeInPixels = 0 draws its image at its own size

The richer dynamic styling API is a separate piece; I will come back on it.

7. Sources on a restyle

  • vector sources are reconciled by object: a source you keep keeps its tiles at any position, so removing one from the middle costs the others nothing
  • a style with no vector source is accepted - raster-only or overlay-only
  • rasters are reconciled by id: an id the style stops declaring is unbound, tiles and textures released; ids you register on RasterTileSources yourself are never touched
  • GpuBasemap.RefreshVectorTiles(source, extent = null) says “the data in this source changed”; false if the map is not drawing it
  • ownership is documented on GpuBasemap.SetStyleAsync: nothing a restyle drops is disposed, a throw changes nothing, the call is not cancellable

The tile grid is still fixed for the life of the host.

Cache drain - TileCache.DrainAsync(token) returns a handle: writes in flight finish, new ones are skipped while you hold it, dispose it to resume. Your draw gate can go.

Thanks,
Ben