Skip to content

Commit

Permalink
add support for raster layers
Browse files Browse the repository at this point in the history
Support raster layers in the theme so that
raster tile images can be combined with
vector tile data.
Raster layers are defined by the style spec
https://docs.mapbox.com/style-spec/reference/layers/#raster
  • Loading branch information
greensopinion committed Jan 14, 2024
1 parent 2cf0aa2 commit 7239af9
Show file tree
Hide file tree
Showing 13 changed files with 138 additions and 8 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 5.2.0

* improve invalid vector tile data handling
* support raster layers in the theme (e.g. to support hillshade with raster tiles)

## 5.1.0

* update dependencies
Expand Down
2 changes: 1 addition & 1 deletion example/ios/Flutter/AppFrameworkInfo.plist
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>12.0</string>
</dict>
</plist>
6 changes: 3 additions & 3 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down Expand Up @@ -349,7 +349,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -398,7 +398,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
Expand Down
2 changes: 1 addition & 1 deletion lib/src/model/tile_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,4 @@ class TileFeature {
}
}

enum TileFeatureType { point, linestring, polygon, background }
enum TileFeatureType { point, linestring, polygon, background, none }
2 changes: 2 additions & 0 deletions lib/src/themes/expression/expression.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class EvaluationContext {
return 'Polygon';
case TileFeatureType.background:
return 'Background';
case TileFeatureType.none:
return 'None';
}
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/src/themes/theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ enum ThemeLayerType {
line,
symbol,
background,
raster,
unsupported
}

Expand Down
71 changes: 71 additions & 0 deletions lib/src/themes/theme_layer_raster.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import 'package:flutter/painting.dart';

import '../../vector_tile_renderer.dart';
import '../context.dart';
import '../features/extensions.dart';
import 'expression/expression.dart';
import 'selector.dart';

class RasterPaintModel {
final Expression<double> opacity;

RasterPaintModel({required this.opacity});
}

class ThemeLayerRaster extends ThemeLayer {
final TileLayerSelector selector;
final RasterPaintModel paintModel;
ThemeLayerRaster(super.id, super.type,
{required this.selector,
required this.paintModel,
required super.minzoom,
required super.maxzoom,
required super.metadata});

@override
void render(Context context) {
final image = context.tileSource.rasterTileset.tiles[tileSource];
if (image != null) {
renderImage(context, image);
}
}

void renderImage(Context context, RasterTile image) {
final evaluationContext = EvaluationContext(
() => {}, TileFeatureType.none, context.logger,
zoom: context.zoom,
zoomScaleFactor: context.zoomScaleFactor,
hasImage: context.hasImage);
final opacity = paintModel.opacity.evaluate(evaluationContext) ?? 1.0;
if (opacity > 0.0) {
final paint = Paint()
..color = Color.fromARGB((opacity * 255).round().clamp(0, 255), 0, 0, 0)
..isAntiAlias = true;
if (image.scope == context.tileSpace) {
context.canvas
.drawImageRect(image.image, image.scope, context.tileSpace, paint);
} else {
final scale = context.tileClip.width / image.scope.width;
context.canvas.drawAtlas(
image.image,
[
RSTransform.fromComponents(
rotation: 0.0,
scale: scale,
anchorX: 0.0,
anchorY: 0.0,
translateX: context.tileClip.left,
translateY: context.tileClip.top),
],
[image.scope],
null,
null,
null,
paint);
}
}
}

@override
String? get tileSource => selector.tileSelector.source;
}
17 changes: 17 additions & 0 deletions lib/src/themes/theme_reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'selector_factory.dart';
import 'style.dart';
import 'text_halo_factory.dart';
import 'theme.dart';
import 'theme_layer_raster.dart';
import 'theme_layers.dart';

class ThemeReader {
Expand Down Expand Up @@ -59,6 +60,8 @@ class ThemeReader {
return _toLineTheme(jsonLayer);
} else if (type == 'symbol') {
return _toSymbolTheme(jsonLayer);
} else if (type == 'raster') {
return _toRasterTheme(jsonLayer);
}
logger.warn(() => 'theme layer type $type not implemented');
return null;
Expand Down Expand Up @@ -100,6 +103,20 @@ class ThemeReader {
return null;
}

ThemeLayer? _toRasterTheme(jsonLayer) {
final selector = selectorFactory.create(jsonLayer);
final paintJson = jsonLayer['paint'];
final opacity = expressionParser.parse(paintJson?['raster-opacity'],
whenNull: () => LiteralExpression(1.0));
return ThemeLayerRaster(
jsonLayer['id'] ?? _unknownId, ThemeLayerType.raster,
selector: selector,
paintModel: RasterPaintModel(opacity: opacity.asDoubleExpression()),
minzoom: _minZoom(jsonLayer),
maxzoom: _maxZoom(jsonLayer),
metadata: _metadata(jsonLayer));
}

ThemeLayer? _toFillTheme(jsonLayer) {
final selector = selectorFactory.create(jsonLayer);
final paintJson = jsonLayer['paint'];
Expand Down
10 changes: 9 additions & 1 deletion lib/src/tile_source.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@ import 'dart:ui';

import 'themes/sprite.dart';
import 'tileset.dart';
import 'tileset_raster.dart';

/// a model representing the source data of a tile.
class TileSource {
/// the tileset of the tile
final Tileset tileset;

/// the raster tileset of the tile, for raster layers of the theme.
final RasterTileset rasterTileset;

/// The optional sprite index, which provides sprites referenced by the theme.
/// If absent, sprites are ignored when rendering.
final SpriteIndex? spriteIndex;
Expand All @@ -16,5 +20,9 @@ class TileSource {
/// If absent, sprites are ignored when rendering.
final Image? spriteAtlas;

TileSource({required this.tileset, this.spriteIndex, this.spriteAtlas});
TileSource(
{required this.tileset,
this.spriteIndex,
this.spriteAtlas,
this.rasterTileset = const RasterTileset(tiles: {})});
}
24 changes: 24 additions & 0 deletions lib/src/tileset_raster.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'dart:ui';

/// A raster tile is an image with a corresponding scope defining the portion of
/// the image that is in scope for the tile.
class RasterTile {
final Image image;
final Rect scope;

RasterTile({required this.image, required this.scope});
}

/// A raster tileset is a collection of raster tiles (images) by `'source'` ID,
/// as defined by the theme.
class RasterTileset {
final Map<String, RasterTile> tiles;

const RasterTileset({required this.tiles});

void dispose() {
for (var tile in tiles.values) {
tile.image.dispose();
}
}
}
1 change: 1 addition & 0 deletions lib/vector_tile_renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ export 'src/themes/theme.dart';
export 'src/themes/theme_reader.dart';
export 'src/tile_source.dart';
export 'src/tileset.dart' hide InternalTileset;
export 'src/tileset_raster.dart';
export 'src/vector_tile_reader.dart';
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: vector_tile_renderer
description: A vector tile renderer for use in creating map tile images or writing to a canvas.
version: 5.1.0
version: 5.2.0
homepage: https://github.com/greensopinion/dart-vector-tile-renderer

environment:
Expand Down
3 changes: 2 additions & 1 deletion test/src/themes/provided_themes_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ void main() {
final theme = ProvidedThemes.lightTheme();
expect(theme.id, 'osm-liberty');
expect(theme.version, '2021-08-22');
expect(theme.tileSources, <String>{'openmaptiles'});
expect(theme.tileSources,
<String>{'openmaptiles', 'natural_earth_shaded_relief'});
});
}

0 comments on commit 7239af9

Please sign in to comment.