vmap 0.27.0
vmap: ^0.27.0 copied to clipboard
A map engine for Flutter drawn on the GPU: any MapLibre style, 3D buildings, terrain and the globe, on every platform.
0.27.0 #
vmap is now a map engine of its own, drawn on the GPU, with flutter_map moved to an adapter package. This release changes most of the public API:
VmapLayer,VmapMap(nowVmapFlutterMap),VmapGesturesandNamedMarkermoved tovmap_flutter_map.VMapandVMapControllerareVmapandVmapController, andMapCameraPositionisVmapCamera, with aLatLngcenter.- Engine settings are grouped in
VmapNetworkOptions(wasTileSourceConfig),VmapRenderOptionsandVmapLabelOptions. - Every zoom is MapLibre's, one below flutter_map's.
onErrorreceives aVmapError, and statistics are inpackage:vmap/debug.dart.interactive,rotate,tiltandkeyboardare onegestures: VmapGestureOptions(...).
The engine #
Vmap, a map widget of vmap's own, takes every setting as a parameter: style, overlay, style edits, feature state, markers that are widgets, pins, text, labels, network and rendering. It tilts, standsfill-extrusionbuildings up, and has gestures for touch, mouse wheel, right drag, trackpad and keyboard, long press, a loading builder, the style's attribution in a corner, and callbacks for camera moves, the camera coming to rest, the style loaded, the map idle and errors.VmapGestureOptionsswitches each gesture off by MapLibre GL JS's handler names (dragPan,scrollZoom,boxZoom,dragRotate,keyboard,doubleClickZoom,touchZoomRotate,touchPitch), withrotate,pitch,inertia,zoomRate,wheelZoomRateandrotateThreshold. It takes the place ofinteractive,rotate,tiltandkeyboard.- The mouse wheel and a trackpad zoom as MapLibre GL JS's scroll zoom does: a wheel and a trackpad told apart by their deltas and timing, a wheel's steps eased, a trackpad followed frame by frame. In a browser two fingers on a trackpad zoom, as does a pinch, which vmap reads from the page's own events, Safari's pinch included.
package:vmap/gestures.dartexports the gesture recognizer behindVmap, so another host can drive its own camera with the same gestures.- Gestures follow MapLibre GL JS: a shift double click zooms out, a shift
drag zooms to the box it draws, a control drag turns and tilts as a right
drag does,
+and-go to the next whole zoom and two with shift, and a right drag in a browser keeps the page's menu away. The pointer is a hand over the map. dart run vmap:setupswitches Flutter GPU on in an app's Android, iOS, macOS, Windows and Linux folders and lets the app load tiles;--checkonly reports, for CI.Vmap.onFeatureTaphands over the topmost feature under a tap, andtappableLayerslimits it to some style layers, so finding what was tapped needs no controller.VmapControllerasks about clusters (clusterExpansionZoom,clusterChildren,clusterLeaves) and downloads regions (downloadRegion) itself, and every method that takes an area takes aVmapBounds.- Styles, overlays and feature states are compared by content, so ones
built afresh in
buildcost nothing while they say the same. VmapControllermoves it:flyTo,easeToandfitBoundsas MapLibre's do,featuresAtandfeaturesIn, andtoScreenandtoLatLng. A moving camera carries its speed into the next animation.VmapCamerasays where the map looks: aLatLngcenter, a zoom in MapLibre's convention, bearing, pitch, roll,EdgeInsetspadding and a field of view in degrees. Every zoom the engine takes or gives is MapLibre's.- The GPU renderer is built in and used by default, the canvas where
Flutter GPU does not run.
VmapEngine.drawingWithsays which draws, and a debug build says which switch is missing. vmap needs Flutter 3.47.1 or newer. It draws buildings with a depth buffer, the style'sterrainwith the map draped over it, the globe, sky and fog. - Engine settings are grouped in
VmapNetworkOptions,VmapRenderOptionsandVmapLabelOptions. Labels take letters from the style's glyph ranges by default, as MapLibre does, with the app's fonts for what ranges cannot shape;VmapGlyphs.fontsuses fonts for all. - Labels drawn from fonts are shaped by vmap's own OpenType shaper, checked glyph for glyph against HarfBuzz, and right-to-left text is ordered by the Unicode bidirectional algorithm, which passes all of Unicode's conformance cases.
- Errors arrive as
VmapError, saying what failed (style, tiles, sprite, fonts, GPU) and its address. - A feature found under a tap carries its geometry; cluster members a
LatLng. - The credit a source's TileJSON asks for is part of
VmapEngine.attributions. VmapLayer,VmapFlutterMap(wasVmapMap),VmapGesturesandNamedMarkerare in thevmap_flutter_mappackage, so vmap no longer depends on flutter_map.- Statistics are in
package:vmap/debug.dart.VmapPalettenames its colours.VmapEngine.useWebWorkersets the web worker, which draws label glyphs as well as building tiles.
The web #
- The map draws about six times faster in Safari: its GPU images are shown in page elements of their own, which the browser lays under Flutter's content, instead of being copied into Flutter's renderer, which Safari does on the CPU. Label glyphs are images Flutter's JavaScript renderer uploads once rather than at every draw.
- The WebAssembly build (
--wasm) draws as the JavaScript one does; it froze its tab before. - Labels are drawn from glyphs in the browser too, as on other platforms, with Kurdish shaped and the style's glyph ranges placed right; road shield numbers and names in Latin script showed blank, and halos came out grey.
- Tiles reach WebGL as the lists they are built in.
- A draw's uniforms reach WebGL as one uniform block bound to a buffer
range, not one
uniform*call per member, which took half the page's time while panning; uniform data is written straight from the lists it is built in. - Text from the style's glyph ranges is drawn on the GPU in the browser too, as on other platforms.
- A frame costs Chrome's GPU process about a fifth of what it did: uniform blocks are small, so WebGL no longer copies a whole block each time a draw writes to it; WebGL state already set is not set again; a draw's uniforms go over in one upload; attachments a pass discards are not stored; and handing a frame to the page swaps buffers rather than copying one. Safari's wait for each frame's image is about a third shorter.
- Label text is drawn into the map's own image when no horizon shows, so a frame hands the page one full-screen image instead of two.
- Zooming fast no longer freezes the page: each tile has buffers of its own on the web, which WebGL writes without waiting for the GPU to finish drawing from them; label glyphs are drawn and made into images in the web worker; and a glyph added to the text atlas sends only the rows it changed.
- The map loads sooner: the style is fetched while vmap's fonts load, tile workers start while the source's TileJSON is asked for, every glyph range and sprite index is fetched once for the page and all workers instead of once by each, the Latin glyph range of each font in the style is asked for before the first tile, and a tile shows while the glyph ranges its names need are still coming. Layers no longer fade in when they first load, only when the zoom crosses into their range.
- The README says how to build and serve for the web:
--wasm, cross-origin isolated, with--no-web-resources-cdn.
Drawing #
- On a flat map the camera keeps to the world, as MapLibre's does: it does not zoom out past a world as tall as the map, nor show past the poles.
- A vector source that names no
maxzoom, in the style or its TileJSON, has tiles down to zoom 22, as the style spec and MapLibre say, not 14. Give the source itsmaxzoomwhen its server stops sooner. - Vector tiles in the MapLibre Tile format (MLT): a
vectorsource with"encoding": "mlt"is read as MapLibre reads it. VmapOverlay.images: pictures of the app's own that the style draws by name, as MapLibre'saddImage.- Every
backgroundlayer is drawn, in the style's order. - Labels are placed at MapLibre's anchors, with its collision circles for road names, and road names lie on the ground of a tilted map.
- A map turning or tilting keeps the labels it shows instead of swapping them for others of the same rank, so labels no longer blink while the map rotates.
- A map laid out with no room yet, as on Android's first frame, draws nothing instead of failing on its markers.
- The map lets go of tiles, label images and GPU memory out of view when the system is short of memory.
VmapLabelOptions.crossSourceCollisions: falselets each source's labels keep clear of their own only, as MapLibre's map option.- Raster tiles fade in over
raster-fade-duration, over the tiles they replace, instead of appearing at once. - A building whose height, base or colour reads
feature-statechanges when the app sets that state, so a tapped building can be highlighted. - See-through buildings are drawn as MapLibre draws them, in the map's own pass: their depth first, then their colour where it is nearest, each pixel once.
- A wheel or trackpad zoom while a fling still glides adds to the glide rather than the two taking turns.
- On the globe:
background-patternis drawn, and the map can be centred on a pole while the polar cap fits on the screen.
0.26.0 #
- On pub.dev:
flutter pub add vmap. - In a browser, tiles are built in a web worker that ships with vmap. There
is nothing to compile or set up;
VmapEngine.webWorker = nullbuilds them on the page instead. The worker is only bundled into web builds.
Breaking:
dart run vmap:build_web_workeris gone. Avmap_worker.jsan app compiled into its ownwebfolder can be deleted.- Needs Flutter 3.41 or newer, for web only assets.
0.25.0 #
Breaking:
VmapLayerStatehands out itsengineand no longer repeats nine of its methods.featuresAt,featuresIn, the cluster lookups,downloadRegion,reload,clearCacheandattributionsare called onkey.currentState!.engine.VmapMapStateis public, so aGlobalKey<VmapMapState>on aVmapMapreaches its engine too.VmapStatskeeps what the last frame drew. Running totals and longest costs moved tostats.timings(aVmapTimings), and label text costs tostats.timings.labels(aVmapLabelWork).resetTimersreplaces the timings.labelsShown, which was never set, is gone.- An app that builds tiles in a web worker compiles
vmap_worker.jsagain withdart run vmap:build_web_worker: the messages the worker reads have changed. - Tiles are kept in the cache directory under new folder names, so tiles kept by an earlier version are fetched again once. Delete the old folders, or the whole directory, to get the space back.
Fixed:
- Changing
icon-rotatebuilds the tiles again. - A marker at a pole lands on the edge of the world instead of off it.
- A source that starts after failing stops reporting the failure in
error. - A cluster lookup that throws answers empty at once instead of after ten seconds.
- A PMTiles archive on a server is asked for with the app's user agent.
- When a server replaces a tile set, the old tiles are deleted from the
cache directory, and
clearCachefinds every tile folder. VmapStats.resetTimersrestarts every running count, including the label totals, the longest unpack and the list of slow paints.- A raster layer that turns from picture tiles into a hillshade over the same source gets fresh tiles.
0.24.0 #
featuresIn(southWest, northEast)on the layer's state and the engine: the features the style draws that touch a box, topmost first. A box that covers more than 16 tiles is read from shallower tiles.- Making every label an image in a browser was measured and not adopted; the numbers are in doc/benchmark.md.
0.23.0 #
text-translatemoves names along a line too, straight and bent.text-translateandicon-translatemay change with the zoom.icon-halo-blursoftens the rim of an SDF icon. Covered by a test of the style reader only; it has not been seen drawn.
0.22.0 #
styleEditsonVmapMapandVmapLayer, andeditson the engine: a list ofVmapStyleEditthat sets a paint or layout property, the filter, the zoom range or the visibility of one style layer by itsid. It works on a style loaded by address. A change of paint keeps the tiles loaded, and labels stay as they are unless a symbol layer was changed.
0.21.1 #
- In a browser's JavaScript build the names along roads are images again and every other label stays text. Turned text was costing about half of the frames a lap skipped: one lap went from 104 frames a second with about 350 skipped to 112 and 114 with 225 and 168.
0.21.0 #
VmapEngine.webWorkeranddart run vmap:build_web_worker: in a browser tiles can be built in a web worker instead of on the page's thread. It is off unless the app sets it, and a script that does not load falls back to the page. Measured on a fast computer it changes nothing; it is meant for slow devices and has not been measured on one.- The frames a browser still skips come from drawing label text, not from building tiles as the notes for 0.17.2 guessed.
0.20.0 #
line-gradient: a line of a GeoJSON source coloured by how far along it each point is, for a route that changes colour on the way. The colours carry on across tile edges.
0.19.0 #
downloadRegionon the layer's state and on the engine fetches the tiles of an area into the cache directory for use offline, vector and raster, skipping what is already there. It reports progress and how many tiles were fetched and failed, and can be stopped.
0.18.0 #
More of the style is read:
imagesources: one picture stretched between its four corners.fill-translate-anchor,line-translate-anchorandcircle-translate-anchor: a move along the screen's sides stays that way when the map is turned.text-translateandicon-translatewith their anchors, for labels at a point.text-max-anglesets how sharp a turn a name along a line may take. A style that does not set it keeps the limit of about 24 degrees.line-bluron a dashed line.icon-halo-colorandicon-halo-widthfor SDF icons.hillshade-accent-color, when the style names one.raster-resampling.
0.17.2 #
- Labels in the default JavaScript build no longer wait on images: each is
drawn as text every frame, with the halo as the text's stroked outline.
It flew the benchmark tour in Chrome at about 104 frames a second, where
0.17.0 managed 25 to 32. The
--wasmbuild and every other platform draw as they did in 0.17.0. - The 119 frames a second claimed for 0.17.1 did not hold up: later runs of the same build gave 23 to 58, because making an image in a browser takes longer the busier the machine is. Shared label pages are gone.
- Text in a script whose font the browser fetches late is drawn once the font arrives, where it used to stay as empty boxes.
0.17.1 #
- Much faster in a browser. Making an image there costs milliseconds, and a label used to get one of its own; labels now share pages of a fixed size that fill over a few frames. In Chrome the benchmark tour went from 25 frames a second to 119 on a 120 Hz screen. Other platforms draw as before.
- The benchmark tour runs in a browser:
--dart-define=TOUR=truewithlib/main_web.dartprints its result to the console.
0.17.0 #
- Runs on the web. Files, requests and workers sit behind one platform
layer, and the numbers that needed 64 bits no longer do. In a browser
there is no disk cache (the browser keeps its own), tiles are built on the
page's thread, and a PMTiles archive has to be at an
https://address. clusterExpansionZoom,clusterChildrenandclusterLeaveson the layer's state and on the engine, for zooming in on a tapped cluster or listing what is in it.VmapLayer.enginetakesminZoomandmaxZoom.- A failed load reports an
HttpFailurewith the status and the address.
0.16.0 #
- Dashed lines are drawn in their own colour. They came out white on some renderers, which is what made the dark styles look full of dotted lines.
line-capround and square shape the dashes too, as in MapLibre.line-blur: a line with a soft edge.- A label halo wider than a pixel is filled in, with no gaps around thin letters.
headerson the map, the layer andTileSourceConfig: sent with every request for a style, its tiles, sprites and archives.onErroron the map, the layer and the engine tells the app of a style, tile or sprite that could not be loaded.reload()andclearCache()on the layer's state and on the engine.clearCachedeletes only what the map itself keeps in the directory.- A source that lists several
tilesaddresses spreads its tiles over them and asks the next when one fails.{s}in an address stands for the hosts a, b and c. minZoomandmaxZoomonVmapLayer.
0.15.0 #
- A tile source's
boundsandminzoom: tiles the source does not have are not asked for. That matters for a tile set of one region. scheme: tmsfor servers that number their rows from the south.attributionson the layer's state and on the engine lists the credit the style's sources ask for.
0.14.0 #
withinanddistance: a layer can keep the features inside a polygon, or within so many metres of a place.withinused to hide everything.hslandhsla,splitandjoin,number-formatwith its fraction digits, comparisons with acollator, andgetandhason an object.
0.13.0 #
text-paddingandicon-padding: how much room a label keeps clear of the others.text-line-heightandtext-justifyfor names of several lines.text-halo-blur: a halo with a soft edge.text-ignore-placement.icon-ignore-placementnow means what the specification says: the icon takes no room from other labels. It used to be read asicon-allow-overlap.
0.12.0 #
text-sizeandicon-sizemay read the feature's properties, so a city can have a larger name than a village from one layer.
0.11.0 #
- A label's colour, opacity and halo, and an icon's opacity and SDF colour, may read
the feature's properties and
feature-state, so a picked place can have its name in another colour.
0.10.0 #
line-join: bevel and miter corners beside the round ones. A line with noline-joinnow has miter corners, which is the default of the style specification. Until now every corner was round.line-offset: a line drawn to one side of where it runs.text-rotatefor the text of a point.background-pattern.- The benchmark app reads
style_file=<path on the device>fromflags.txt, so a lap can run with any style without a rebuild.
0.9.0 #
- Heatmaps follow their whole colour ramp. Points add up to a density the way MapLibre's do, across tile edges, and the density is coloured stop by stop.
clusterProperties: a cluster adds up, or takes the largest of, a property of its points, for the style to read.
0.8.0 #
text-variable-anchorandtext-radial-offset: a name that does not fit on one side of its point is tried on the others, and stays on the side it found while it still fits.icon-anchorandicon-offset.symbol-sort-key: of two labels in a layer wanting the same room, the one with the lower number gets it.
0.7.0 #
- PMTiles: a source whose address starts with
pmtiles://is read from one archive file, over HTTP range requests or from the device, for vector, raster and hillshade sources. A file on the device makes an offline map. - Raster layers read
raster-hue-rotate,raster-saturation,raster-contrastandraster-brightness-minand-max. fill-translate,line-translateandcircle-translate.promoteIdon a source andgenerateIdon a GeoJSON source, so features without an id of their own can be given state.
0.6.1 #
- Hillshade tiles meet without a line: each is shaded with the heights of the tiles around it and blends into them, and the shading is no longer doubled where two tiles touch.
0.6.0 #
- GeoJSON sources cluster their points:
cluster,clusterRadius,clusterMaxZoomandclusterMinPoints, withpoint_countand the other cluster properties for the style to read. featureStatesand thefeature-stateexpression: paint a feature by state the app gives it, such as the one that was tapped, without rebuilding tiles.VmapFeaturecarries the feature'sid.- Circles are no longer cut where two tiles meet.
- Two points of an app's own data a few pixels apart can carry the same text. Only one of them was labelled.
0.5.1 #
- A road name on a bend is laid along the road every frame, so its letters keep their spacing while the map zooms. They used to drift apart until the labels were next placed.
- Changing the data of an overlay source keeps what was drawn until the new data is ready, and leaves the map's labels alone when the overlay's layers are the same. A moving dot no longer blinks.
0.5.0 #
- Rotation: two fingers turn the map in
VmapGesturesandVmapMap, and labels are laid out upright on a turned map. - Every vector source of a style is drawn, not only the busiest one, and layers keep the style's order across sources and raster layers.
- GeoJSON sources, inline or by address.
VmapOverlay: sources and layers of your own on top of any style.onFeatureTapandfeaturesAt: what the style draws at a point.- Road names bend along curves.
curvedLabels: falseturns it off. - Polygons are labelled.
hillshadeandheatmaplayers,line-pattern, andicon-colorfor SDF icons.- More expressions:
let,var,slice,index-of,typeof,id, trigonometry and cubic-bezier interpolation. VmapLayer's state class is public asVmapLayerState.
0.4.0 #
VmapMap: a whole map in one widget, withVmapGesturesin place of flutter_map's gestures.pins: places of interest drawn as round pins instead of the style's icons.VmapPins.standardcovers the place classes of OpenMapTiles styles.- The built in style names places of interest from zoom 15.5.
- vmap now depends on latlong2 directly.
0.3.2 #
- A frame that places labels draws no new text images, and a frame draws about a millisecond of them at most, so the two costs never land together. On the test phone the slowest 1 percent of UI frames went from about 8.5 ms to about 7 ms.
- Labels are named on the worker isolates and handed to the placer in steps.
- Meshes are cut at 16000 vertices, so no single upload or re-widening is long.
tool/quick_bench.shreruns the tour on a device in half a minute without a rebuild.
0.3.1 #
- Label images are kept for 1400 labels instead of 500, so coming back to a place does not draw its names again.
- A frame walks only the labels that are showing or fading.
0.3.0 #
- Icons:
icon-imagefrom the style's sprite sheet, with text placed bytext-anchorandtext-offset, road shields, and arrows repeated along a line. - Real dashes for
line-dasharray. fill-outline-color,fill-pattern,circlelayers andrasterlayers.- Fill, line and circle paint may read a feature's properties.
text-anchorandtext-offsetnow move every point label, so a town's name sits above its point as the style asks.
0.2.0 #
- Draws any MapLibre style:
VmapStyle.url,VmapStyle.json, or the built inVmapStyle.lightandVmapStyle.dark. The style names the tile source. VmapLayergoes where aTileLayergoes and owns its engine. UseVmapLayer.engineto share one.- Markers are flutter_map
Markers.NamedMarkeradds a name the map writes beside it.VmapMarkeris gone. - Fonts are given as a
TextStyle.VmapLabelFont,TileSourceConfig.nameKeysandTileSourceConfig.tileJsonUrlare gone: name keys and the tile source belong to the style. - The example is a small showcase, and the benchmark runs both renderers on the same style.
0.1.0 #
- First release: a vector tile layer for flutter_map drawn with the Flutter canvas, with label placement, marker names, a light and a dark palette, a disk cache and its own gesture handling.
