diff --git a/src/libs/Mapbox.Maui/IMapboxView.cs b/src/libs/Mapbox.Maui/IMapboxView.cs index c3630a6..ae2def8 100644 --- a/src/libs/Mapbox.Maui/IMapboxView.cs +++ b/src/libs/Mapbox.Maui/IMapboxView.cs @@ -101,6 +101,7 @@ void CameraForCoordinates( double? maxZoom = default, ScreenPosition? offset = default ); + void SetSourcePropertyFor(string sourceId, string propertyName, T value, Action completion = default); } public class MapTappedEventArgs : EventArgs diff --git a/src/libs/Mapbox.Maui/Mapbox.Maui.csproj b/src/libs/Mapbox.Maui/Mapbox.Maui.csproj index 868e3d4..a5db06a 100644 --- a/src/libs/Mapbox.Maui/Mapbox.Maui.csproj +++ b/src/libs/Mapbox.Maui/Mapbox.Maui.csproj @@ -61,7 +61,7 @@ https://github.com/tuyen-vuduc/mapbox-maui https://mapbox.tuyen-vuduc.tech false - 11.5.1-alpha05 + 11.5.1-alpha06 README.md LICENSE tv-mapbox.png @@ -90,7 +90,7 @@ - + diff --git a/src/libs/Mapbox.Maui/Models/Styles/Layers/LayerType.cs b/src/libs/Mapbox.Maui/Models/Styles/Layers/LayerType.cs index f923c95..5c46024 100644 --- a/src/libs/Mapbox.Maui/Models/Styles/Layers/LayerType.cs +++ b/src/libs/Mapbox.Maui/Models/Styles/Layers/LayerType.cs @@ -27,6 +27,9 @@ namespace MapboxMaui; /// Raster map textures such as satellite imagery. public static readonly LayerType Raster = new ("raster"); + /// Layer repsenting particles on the map. + public static readonly LayerType RasterParticle = new ("raster-particle"); + /// Client-side hillshading visualization based on DEM data. /// Currently, the implementation only supports Mapbox Terrain RGB and Mapzen Terrarium tiles. public static readonly LayerType Hillshade = new ("hillshade"); diff --git a/src/libs/Mapbox.Maui/Models/Styles/Layers/MapboxLayer.cs b/src/libs/Mapbox.Maui/Models/Styles/Layers/MapboxLayer.cs index 298aac3..a55ea05 100644 --- a/src/libs/Mapbox.Maui/Models/Styles/Layers/MapboxLayer.cs +++ b/src/libs/Mapbox.Maui/Models/Styles/Layers/MapboxLayer.cs @@ -23,6 +23,7 @@ public static class MapboxLayerKey public const string filter = "filter"; public const string source = "source"; public const string sourceLayer = "source-layer"; + public const string slot = "slot"; public const string minZoom = "minzoom"; public const string maxZoom = "maxzoom"; public const string layout = "layout"; @@ -91,6 +92,13 @@ public string SourceLayer set => SetProperty(MapboxLayerKey.sourceLayer, value); } + // /// The slot this layer is assigned to. If specified, and a slot with that name exists, it will be placed at that position in the layer order. + public string Slot + { + get => GetProperty(MapboxLayerKey.slot, default); + set => SetProperty(MapboxLayerKey.slot, value); + } + public double? MinZoom { get => GetProperty(MapboxLayerKey.minZoom, default); diff --git a/src/libs/Mapbox.Maui/Models/Styles/Layers/RasterLayer.cs b/src/libs/Mapbox.Maui/Models/Styles/Layers/RasterLayer.cs new file mode 100644 index 0000000..030a4d0 --- /dev/null +++ b/src/libs/Mapbox.Maui/Models/Styles/Layers/RasterLayer.cs @@ -0,0 +1,378 @@ +namespace MapboxMaui.Styles; + +public partial class RasterLayer : MapboxLayer +{ + public RasterLayer(string id, string source) + : base(id) + { + Type = LayerType.Raster; + Visibility = new PropertyValue(MapboxMaui.Visibility.Visible); + Source = source; + } + + // /// Displayed band of raster array source layer. Defaults to the first band if not set. + // @_documentation(visibility: public) + // @_spi(Experimental) public var rasterArrayBand: Value? + + /// Increase or reduce the brightness of the image. The value is the maximum brightness. + /// Default value: 1. Value range: [0, 1] + public PropertyValue RasterBrightnessMax + { + get => GetProperty>( + RasterLayerKey.rasterBrightnessMax, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterBrightnessMax, + value, + MapboxLayerKey.paint + ); + } + + /// Transition options for `rasterBrightnessMax`. + public PropertyValue RasterBrightnessMaxTransition + { + get => GetProperty>( + RasterLayerKey.rasterBrightnessMaxTransition, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterBrightnessMaxTransition, + value, + MapboxLayerKey.paint + ); + } + + /// Increase or reduce the brightness of the image. The value is the minimum brightness. + /// Default value: 0. Value range: [0, 1] + public PropertyValue RasterBrightnessMin + { + get => GetProperty>( + RasterLayerKey.rasterBrightnessMin, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterBrightnessMin, + value, + MapboxLayerKey.paint + ); + } + + /// Transition options for `rasterBrightnessMin`. + public PropertyValue RasterBrightnessMinTransition + { + get => GetProperty>( + RasterLayerKey.rasterBrightnessMinTransition, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterBrightnessMinTransition, + value, + MapboxLayerKey.paint + ); + } + + /// Defines a color map by which to colorize a raster layer, parameterized by the `["raster-value"]` expression and evaluated at 256 uniformly spaced steps over the range specified by `raster-color-range`. + public PropertyValue RasterColor + { + get => GetProperty>( + RasterLayerKey.rasterColor, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterColor, + value, + MapboxLayerKey.paint + ); + } + + /// When `raster-color` is active, specifies the combination of source RGB channels used to compute the raster value. Computed using the equation `mix.r - src.r + mix.g - src.g + mix.b - src.b + mix.a`. The first three components specify the mix of source red, green, and blue channels, respectively. The fourth component serves as a constant offset and is -not- multipled by source alpha. Source alpha is instead carried through and applied as opacity to the colorized result. Default value corresponds to RGB luminosity. + /// Default value: [0.2126,0.7152,0.0722,0]. + public PropertyValue RasterColorMix + { + get => GetProperty>( + RasterLayerKey.rasterColorMix, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterColorMix, + value, + MapboxLayerKey.paint + ); + } + + /// Transition options for `rasterColorMix`. + public PropertyValue RasterColorMixTransition + { + get => GetProperty>( + RasterLayerKey.rasterColorMixTransition, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterColorMixTransition, + value, + MapboxLayerKey.paint + ); + } + + /// When `raster-color` is active, specifies the range over which `raster-color` is tabulated. Units correspond to the computed raster value via `raster-color-mix`. For `rasterarray` sources, if `raster-color-range` is unspecified, the source's stated data range is used. + public PropertyValue RasterColorRange + { + get => GetProperty>( + RasterLayerKey.rasterColorRange, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterColorRange, + value, + MapboxLayerKey.paint + ); + } + + /// Transition options for `rasterColorRange`. + public PropertyValue RasterColorRangeTransition + { + get => GetProperty>( + RasterLayerKey.rasterColorRangeTransition, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterColorRangeTransition, + value, + MapboxLayerKey.paint + ); + } + + /// Increase or reduce the contrast of the image. + /// Default value: 0. Value range: [-1, 1] + public PropertyValue RasterContrast + { + get => GetProperty>( + RasterLayerKey.rasterContrast, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterContrast, + value, + MapboxLayerKey.paint + ); + } + + /// Transition options for `rasterContrast`. + public PropertyValue RasterContrastTransition + { + get => GetProperty>( + RasterLayerKey.rasterContrastTransition, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterContrastTransition, + value, + MapboxLayerKey.paint + ); + } + + // /// Specifies an uniform elevation from the ground, in meters. + // /// Default value: 0. Minimum value: 0. + // @_documentation(visibility: public) + // @_spi(Experimental) public var rasterElevation: Value? + + // /// Transition options for `rasterElevation`. + // @_documentation(visibility: public) + // @_spi(Experimental) public var rasterElevationTransition: StyleTransition? + + /// Controls the intensity of light emitted on the source features. + /// Default value: 0. Minimum value: 0. + public PropertyValue RasterEmissiveStrength + { + get => GetProperty>( + RasterLayerKey.rasterEmissiveStrength, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterEmissiveStrength, + value, + MapboxLayerKey.paint + ); + } + + /// Transition options for `rasterEmissiveStrength`. + public PropertyValue RasterEmissiveStrengthTransition + { + get => GetProperty>( + RasterLayerKey.rasterEmissiveStrengthTransition, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterEmissiveStrengthTransition, + value, + MapboxLayerKey.paint + ); + } + + /// Fade duration when a new tile is added. + /// Default value: 300. Minimum value: 0. + public PropertyValue RasterFadeDuration + { + get => GetProperty>( + RasterLayerKey.rasterFadeDuration, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterFadeDuration, + value, + MapboxLayerKey.paint + ); + } + + /// Rotates hues around the color wheel. + /// Default value: 0. + public PropertyValue RasterHueRotate + { + get => GetProperty>( + RasterLayerKey.rasterHueRotate, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterHueRotate, + value, + MapboxLayerKey.paint + ); + } + + /// Transition options for `rasterHueRotate`. + public PropertyValue RasterHueRotateTransition + { + get => GetProperty>( + RasterLayerKey.rasterHueRotateTransition, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterHueRotateTransition, + value, + MapboxLayerKey.paint + ); + } + + /// The opacity at which the image will be drawn. + /// Default value: 1. Value range: [0, 1] + public PropertyValue RasterOpacity + { + get => GetProperty>( + RasterLayerKey.rasterOpacity, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterOpacity, + value, + MapboxLayerKey.paint + ); + } + + /// Transition options for `rasterOpacity`. + public PropertyValue RasterOpacityTransition + { + get => GetProperty>( + RasterLayerKey.rasterOpacityTransition, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterOpacityTransition, + value, + MapboxLayerKey.paint + ); + } + + /// The resampling/interpolation method to use for overscaling, also known as texture magnification filter + /// Default value: "linear". + public PropertyValue RasterResampling + { + get => GetProperty>( + RasterLayerKey.rasterResampling, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterResampling, + value, + MapboxLayerKey.paint + ); + } + + /// Increase or reduce the saturation of the image. + /// Default value: 0. Value range: [-1, 1] + public PropertyValue RasterSaturation + { + get => GetProperty>( + RasterLayerKey.rasterSaturation, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterSaturation, + value, + MapboxLayerKey.paint + ); + } + + /// Transition options for `rasterSaturation`. + public PropertyValue RasterSaturationTransition + { + get => GetProperty>( + RasterLayerKey.rasterSaturationTransition, + default, + MapboxLayerKey.paint + ); + set => SetProperty( + RasterLayerKey.rasterSaturationTransition, + value, + MapboxLayerKey.paint + ); + } + + static class RasterLayerKey + { + public const string rasterArrayBand = "raster-array-band"; + public const string rasterBrightnessMax = "raster-brightness-max"; + public const string rasterBrightnessMaxTransition = "raster-brightness-max-transition"; + public const string rasterBrightnessMin = "raster-brightness-min"; + public const string rasterBrightnessMinTransition = "raster-brightness-min-transition"; + public const string rasterColor = "raster-color"; + public const string rasterColorMix = "raster-color-mix"; + public const string rasterColorMixTransition = "raster-color-mix-transition"; + public const string rasterColorRange = "raster-color-range"; + public const string rasterColorRangeTransition = "raster-color-range-transition"; + public const string rasterContrast = "raster-contrast"; + public const string rasterContrastTransition = "raster-contrast-transition"; + public const string rasterElevation = "raster-elevation"; + public const string rasterElevationTransition = "raster-elevation-transition"; + public const string rasterEmissiveStrength = "raster-emissive-strength"; + public const string rasterEmissiveStrengthTransition = "raster-emissive-strength-transition"; + public const string rasterFadeDuration = "raster-fade-duration"; + public const string rasterHueRotate = "raster-hue-rotate"; + public const string rasterHueRotateTransition = "raster-hue-rotate-transition"; + public const string rasterOpacity = "raster-opacity"; + public const string rasterOpacityTransition = "raster-opacity-transition"; + public const string rasterResampling = "raster-resampling"; + public const string rasterSaturation = "raster-saturation"; + public const string rasterSaturationTransition = "raster-saturation-transition"; + } +} \ No newline at end of file diff --git a/src/libs/Mapbox.Maui/Models/Styles/Sources/RasterSource.cs b/src/libs/Mapbox.Maui/Models/Styles/Sources/RasterSource.cs new file mode 100644 index 0000000..2d087c8 --- /dev/null +++ b/src/libs/Mapbox.Maui/Models/Styles/Sources/RasterSource.cs @@ -0,0 +1,233 @@ +namespace MapboxMaui.Styles; + +public class RasterSource : MapboxSource +{ + public RasterSource(string id) + : base(id, "raster") + { + } + + private static class RasterSourceKey + { + public const string url = "url"; + public const string tiles = "tiles"; + public const string bounds = "bounds"; + public const string minzoom = "minzoom"; + public const string maxzoom = "maxzoom"; + public const string tileSize = "tileSize"; + public const string scheme = "scheme"; + public const string attribution = "attribution"; + public const string @volatile = "volatile"; + public const string prefetchZoomDelta = "prefetch-zoom-delta"; + public const string tileCacheBudget = "tile-cache-budget"; + public const string minimumTileUpdateInterval = "minimum-tile-update-interval"; + public const string maxOverscaleFactorForParentTiles = "max-overscale-factor-for-parent-tiles"; + public const string tileRequestsDelay = "tile-requests-delay"; + public const string tileNetworkRequestsDelay = "tile-network-requests-delay"; + } + + /// A URL to a TileJSON resource. Supported protocols are `http:`, `https:`, and `mapbox://`. Required if `tiles` is not provided. + public string Url + { + get => GetProperty( + RasterSourceKey.url, + default + ); + set => SetProperty( + RasterSourceKey.url, + value + ); + } + + /// An array of one or more tile source URLs, as in the TileJSON spec. Required if `url` is not provided. + public string[] Tiles + { + get => GetProperty( + RasterSourceKey.tiles, + default + ); + set => SetProperty( + RasterSourceKey.tiles, + value + ); + } + + /// An array containing the longitude and latitude of the southwest and northeast corners of the source's bounding box in the following order: `[sw.lng, sw.lat, ne.lng, ne.lat]`. When this property is included in a source, no tiles outside of the given bounds are requested by Mapbox GL. + /// Default value: [-180,-85.051129,180,85.051129]. + public double?[] Bounds + { + get => GetProperty( + RasterSourceKey.bounds, + default + ); + set => SetProperty( + RasterSourceKey.bounds, + value + ); + } + + /// Minimum zoom level for which tiles are available, as in the TileJSON spec. + /// Default value: 0. + public double? Minzoom + { + get => GetProperty( + RasterSourceKey.minzoom, + default + ); + set => SetProperty( + RasterSourceKey.minzoom, + value + ); + } + + /// Maximum zoom level for which tiles are available, as in the TileJSON spec. Data from tiles at the maxzoom are used when displaying the map at higher zoom levels. + /// Default value: 22. + public double? Maxzoom + { + get => GetProperty( + RasterSourceKey.maxzoom, + default + ); + set => SetProperty( + RasterSourceKey.maxzoom, + value + ); + } + + /// The minimum visual size to display tiles for this layer. Only configurable for raster layers. + /// Default value: 512. + public double? TileSize + { + get => GetProperty( + RasterSourceKey.tileSize, + default + ); + set => SetProperty( + RasterSourceKey.tileSize, + value + ); + } + + /// Influences the y direction of the tile coordinates. The global-mercator (aka Spherical Mercator) profile is assumed. + /// Default value: "xyz". + public Scheme? Scheme + { + get => GetProperty( + RasterSourceKey.scheme, + default + ); + set => SetProperty( + RasterSourceKey.scheme, + value + ); + } + + /// Contains an attribution to be displayed when the map is shown to a user. + public string Attribution + { + get => GetProperty( + RasterSourceKey.attribution, + default + ); + set => SetProperty( + RasterSourceKey.attribution, + value + ); + } + + /// A setting to determine whether a source's tiles are cached locally. + /// Default value: false. + public bool? Volatile + { + get => GetProperty( + RasterSourceKey.@volatile, + default + ); + set => SetProperty( + RasterSourceKey.@volatile, + value + ); + } + + /// When loading a map, if PrefetchZoomDelta is set to any number greater than 0, the map will first request a tile at zoom level lower than zoom - delta, but so that the zoom level is multiple of delta, in an attempt to display a full map at lower resolution as quick as possible. It will get clamped at the tile source minimum zoom. + /// Default value: 4. + public double? PrefetchZoomDelta + { + get => GetProperty( + RasterSourceKey.prefetchZoomDelta, + default + ); + set => SetProperty( + RasterSourceKey.prefetchZoomDelta, + value + ); + } + + /// This property defines a source-specific resource budget, either in tile units or in megabytes. Whenever the tile cache goes over the defined limit, the least recently used tile will be evicted from the in-memory cache. Note that the current implementation does not take into account resources allocated by the visible tiles. + public TileCacheBudgetSize TileCacheBudget + { + get => GetProperty( + RasterSourceKey.tileCacheBudget, + default + ); + set => SetProperty( + RasterSourceKey.tileCacheBudget, + value + ); + } + + /// Minimum tile update interval in seconds, which is used to throttle the tile update network requests. If the given source supports loading tiles from a server, sets the minimum tile update interval. Update network requests that are more frequent than the minimum tile update interval are suppressed. + /// Default value: 0. + public double? MinimumTileUpdateInterval + { + get => GetProperty( + RasterSourceKey.minimumTileUpdateInterval, + default + ); + set => SetProperty( + RasterSourceKey.minimumTileUpdateInterval, + value + ); + } + + /// When a set of tiles for a current zoom level is being rendered and some of the ideal tiles that cover the screen are not yet loaded, parent tile could be used instead. This might introduce unwanted rendering side-effects, especially for raster tiles that are overscaled multiple times. This property sets the maximum limit for how much a parent tile can be overscaled. + public double? MaxOverscaleFactorForParentTiles + { + get => GetProperty( + RasterSourceKey.maxOverscaleFactorForParentTiles, + default + ); + set => SetProperty( + RasterSourceKey.maxOverscaleFactorForParentTiles, + value + ); + } + + /// For the tiled sources, this property sets the tile requests delay. The given delay comes in action only during an ongoing animation or gestures. It helps to avoid loading, parsing and rendering of the transient tiles and thus to improve the rendering performance, especially on low-end devices. + /// Default value: 0. + public double? TileRequestsDelay + { + get => GetProperty( + RasterSourceKey.tileRequestsDelay, + default + ); + set => SetProperty( + RasterSourceKey.tileRequestsDelay, + value + ); + } + + /// For the tiled sources, this property sets the tile network requests delay. The given delay comes in action only during an ongoing animation or gestures. It helps to avoid loading the transient tiles from the network and thus to avoid redundant network requests. Note that tile-network-requests-delay value is superseded with tile-requests-delay property value, if both are provided. + /// Default value: 0. + public double? TileNetworkRequestsDelay + { + get => GetProperty( + RasterSourceKey.tileNetworkRequestsDelay, + default + ); + set => SetProperty( + RasterSourceKey.tileNetworkRequestsDelay, + value + ); + } +} \ No newline at end of file diff --git a/src/libs/Mapbox.Maui/Models/Styles/Types/TileCacheBudgetSize.cs b/src/libs/Mapbox.Maui/Models/Styles/Types/TileCacheBudgetSize.cs new file mode 100644 index 0000000..99ab3f1 --- /dev/null +++ b/src/libs/Mapbox.Maui/Models/Styles/Types/TileCacheBudgetSize.cs @@ -0,0 +1,17 @@ +namespace MapboxMaui; + +public record TileCacheBudgetSize +{ + public int? Tiles { get; } + public int? Megabytes { get; } + + private TileCacheBudgetSize(int? tiles, int? megabytes) { + Tiles = tiles; + Megabytes = megabytes; + } + + public static TileCacheBudgetSize FromTiles(int tiles) + => new (tiles, null); + public static TileCacheBudgetSize FromMegaBytes(int megabytes) + => new (null, megabytes); +} \ No newline at end of file diff --git a/src/libs/Mapbox.Maui/Platforms/Android/AdditionalExtensions.cs b/src/libs/Mapbox.Maui/Platforms/Android/AdditionalExtensions.cs index 2671859..f2fe981 100644 --- a/src/libs/Mapbox.Maui/Platforms/Android/AdditionalExtensions.cs +++ b/src/libs/Mapbox.Maui/Platforms/Android/AdditionalExtensions.cs @@ -188,6 +188,10 @@ internal static PlatformValue Wrap(this object xvalue, bool rgba = false) float value => new PlatformValue(value), double value => new PlatformValue(value), string value => new PlatformValue(value), + TileCacheBudgetSize value => new PlatformValue( + value.Tiles + ?? value.Megabytes + ?? 0), Color value => rgba ? new PlatformValue(value.ToRgbaString()) : new PlatformValue(value.ToInt()), diff --git a/src/libs/Mapbox.Maui/Platforms/Android/MapboxViewHandler.Controller.cs b/src/libs/Mapbox.Maui/Platforms/Android/MapboxViewHandler.Controller.cs index ab940a0..4e93757 100644 --- a/src/libs/Mapbox.Maui/Platforms/Android/MapboxViewHandler.Controller.cs +++ b/src/libs/Mapbox.Maui/Platforms/Android/MapboxViewHandler.Controller.cs @@ -1,5 +1,6 @@  +using Com.Mapbox.Bindgen; using Com.Mapbox.Functions; namespace MapboxMaui; @@ -42,22 +43,6 @@ public ScreenPosition GetScreenPosition(IPosition position) return coords.ToX(); } - /** - * Convenience method that returns the [CameraOptions] object for given parameters. - * - * Note: if the render thread did not yet calculate the size of the map (due to initialization or map resizing) - empty [CameraOptions] will be returned. - * Emptiness could be checked with [CameraOptions.isEmpty]. Consider using asynchronous overloaded method. - * - * @param coordinates The `coordinates` representing the bounds of the camera. - * @param camera The [CameraOptions] which will be applied before calculating the camera for the coordinates. If any of the fields in [CameraOptions] are not provided then the current value from the map for that field will be used. - * @param coordinatesPadding The amount of padding in pixels to add to the given `coordinates`. - * Note: This padding is not applied to the map but to the coordinates provided. If you want to apply padding to the map use param `camera`. - * @param maxZoom The maximum zoom level allowed in the returned camera options. - * @param offset The center of the given bounds relative to map center in pixels. - * - * @return The [CameraOptions] object representing the provided parameters if the map size was calculated and empty [CameraOptions] otherwise, see [CameraOptions.isEmpty]. - * Also empty [CameraOptions] are returned in case of an internal error. - */ public CameraOptions? CameraForCoordinates( IEnumerable coordinates, CameraOptions? cameraOptions = null, @@ -79,17 +64,6 @@ public ScreenPosition GetScreenPosition(IPosition position) return result.ToX(); } - /** - * Convenience method that returns the [CameraOptions] object for given parameters. - * - * @param coordinates The `coordinates` representing the bounds of the camera. - * @param camera The [CameraOptions] which will be applied before calculating the camera for the coordinates. If any of the fields in [CameraOptions] are not provided then the current value from the map for that field will be used. - * @param coordinatesPadding The amount of padding in pixels to add to the given `coordinates`. - * Note: This padding is not applied to the map but to the coordinates provided. If you want to apply padding to the map use param `camera`. - * @param maxZoom The maximum zoom level allowed in the returned camera options. - * @param offset The center of the given bounds relative to map center in pixels. - * @param completion Callback returning the [CameraOptions] object representing the provided parameters. Those [CameraOptions] always take into account actual MapView size and may return empty ([CameraOptions.isEmpty]) options only if an internal error has occurred. - */ public void CameraForCoordinates( IEnumerable coordinates, Action completion, @@ -102,7 +76,7 @@ public void CameraForCoordinates( if (mapView is null) { - completion(null); + completion?.Invoke(null); return; } @@ -118,4 +92,43 @@ public void CameraForCoordinates( }) ); } + + public void SetSourcePropertyFor( + string sourceId, string propertyName, + T value, Action completion = null) + { + var mapView = mapboxFragment?.MapView; + + if (mapView is null) + { + completion?.Invoke(null); + return; + } + + var result = mapView.MapboxMap.SetStyleSourceProperty(sourceId, propertyName, value.Wrap()); + + result.OnError(new XExpectedAction(error => + { + completion?.Invoke(new Exception("An error occurred when setting source property")); + })); + result.OnValue(new XExpectedAction(result => + { + completion?.Invoke(null); + })); + } +} + +internal class XExpectedAction : Java.Lang.Object, Expected.IAction +{ + private readonly Action action; + + public XExpectedAction(Action action) + { + this.action = action; + } + + public void Run(Java.Lang.Object p0) + { + action?.Invoke(p0); + } } diff --git a/src/libs/Mapbox.Maui/Platforms/iOS/AdditionalExtensions.cs b/src/libs/Mapbox.Maui/Platforms/iOS/AdditionalExtensions.cs index 17ad277..2bc6c8a 100644 --- a/src/libs/Mapbox.Maui/Platforms/iOS/AdditionalExtensions.cs +++ b/src/libs/Mapbox.Maui/Platforms/iOS/AdditionalExtensions.cs @@ -198,6 +198,10 @@ internal static NSObject Wrap(this object xvalue) float value => NSNumber.FromDouble(value), double value => NSNumber.FromDouble(value), string value => new NSString(value), + TileCacheBudgetSize value => NSNumber.FromDouble( + value.Tiles + ?? value.Megabytes + ?? 0), Color value => new NSString(value.ToRgbaString()), INamedString value => new NSString(value.Value), IPropertyValue value => value.Value is DslExpression expression1 diff --git a/src/libs/Mapbox.Maui/Platforms/iOS/MapboxViewHandler.Controller.cs b/src/libs/Mapbox.Maui/Platforms/iOS/MapboxViewHandler.Controller.cs index 7bfdfe6..f1e06b4 100644 --- a/src/libs/Mapbox.Maui/Platforms/iOS/MapboxViewHandler.Controller.cs +++ b/src/libs/Mapbox.Maui/Platforms/iOS/MapboxViewHandler.Controller.cs @@ -1,4 +1,5 @@ -using MapboxMapsObjC; +using Foundation; +using MapboxMapsObjC; using Microsoft.Maui.Platform; namespace MapboxMaui; @@ -20,6 +21,7 @@ public CoordinateBounds GetCoordinateBoundsForCamera(CameraOptions cameraOptions xbounds.InfiniteBounds ); } + public IPosition GetMapPosition(ScreenPosition position) { var mapView = PlatformView.MapView; @@ -43,22 +45,6 @@ public ScreenPosition GetScreenPosition(IPosition position) return coords.ToPoint(); } - /** - * Convenience method that returns the [CameraOptions] object for given parameters. - * - * Note: if the render thread did not yet calculate the size of the map (due to initialization or map resizing) - empty [CameraOptions] will be returned. - * Emptiness could be checked with [CameraOptions.isEmpty]. Consider using asynchronous overloaded method. - * - * @param coordinates The `coordinates` representing the bounds of the camera. - * @param camera The [CameraOptions] which will be applied before calculating the camera for the coordinates. If any of the fields in [CameraOptions] are not provided then the current value from the map for that field will be used. - * @param coordinatesPadding The amount of padding in pixels to add to the given `coordinates`. - * Note: This padding is not applied to the map but to the coordinates provided. If you want to apply padding to the map use param `camera`. - * @param maxZoom The maximum zoom level allowed in the returned camera options. - * @param offset The center of the given bounds relative to map center in pixels. - * - * @return The [CameraOptions] object representing the provided parameters if the map size was calculated and empty [CameraOptions] otherwise, see [CameraOptions.isEmpty]. - * Also empty [CameraOptions] are returned in case of an internal error. - */ public CameraOptions? CameraForCoordinates( IEnumerable coordinates, CameraOptions? cameraOptions = null, @@ -84,17 +70,6 @@ public ScreenPosition GetScreenPosition(IPosition position) return xresult?.ToX(); } - /** - * Convenience method that returns the [CameraOptions] object for given parameters. - * - * @param coordinates The `coordinates` representing the bounds of the camera. - * @param camera The [CameraOptions] which will be applied before calculating the camera for the coordinates. If any of the fields in [CameraOptions] are not provided then the current value from the map for that field will be used. - * @param coordinatesPadding The amount of padding in pixels to add to the given `coordinates`. - * Note: This padding is not applied to the map but to the coordinates provided. If you want to apply padding to the map use param `camera`. - * @param maxZoom The maximum zoom level allowed in the returned camera options. - * @param offset The center of the given bounds relative to map center in pixels. - * @param completion Callback returning the [CameraOptions] object representing the provided parameters. Those [CameraOptions] always take into account actual MapView size and may return empty ([CameraOptions.isEmpty]) options only if an internal error has occurred. - */ public void CameraForCoordinates( IEnumerable coordinates, Action completion, @@ -108,7 +83,7 @@ public void CameraForCoordinates( if (mapView == null) { - completion(null); + completion?.Invoke(null); return; } @@ -120,7 +95,32 @@ public void CameraForCoordinates( offset?.ToNSValue(), (result, _) => { - completion(result?.ToX()); + completion?.Invoke(result?.ToX()); + }); + } + + public void SetSourcePropertyFor( + string sourceId, string propertyName, + T value, Action completion = null) + { + var mapView = PlatformView.MapView; + + if (mapView == null) + { + completion?.Invoke(null); + return; + } + + mapView.MapboxMap().SetSourcePropertyFor( + sourceId, + propertyName, + value.Wrap(), + (error) => + { + var exception = error is not null + ? new NSErrorException(error) + : null; + completion?.Invoke(exception); }); } } diff --git a/src/qs/MapboxMauiQs/Examples/Styles/73.RasterTileSource/RasterTileSourceExample.cs b/src/qs/MapboxMauiQs/Examples/Styles/73.RasterTileSource/RasterTileSourceExample.cs new file mode 100644 index 0000000..ca6551f --- /dev/null +++ b/src/qs/MapboxMauiQs/Examples/Styles/73.RasterTileSource/RasterTileSourceExample.cs @@ -0,0 +1,99 @@ +namespace MapboxMauiQs; + +public class RasterTileSourceExample : ContentPage, IExamplePage, IQueryAttributable +{ + private const string sourceId = "raster-source"; + + readonly MapboxView map; + IExampleInfo info; + bool isTileRequestDelayEnabled; + readonly Button toggleTitleRequestButton; + + public RasterTileSourceExample() + { + iOSPage.SetUseSafeArea(this, false); + Content = new Grid + { + Children = + { + (map = new MapboxView()), + (toggleTitleRequestButton = new Button + { + HorizontalOptions = LayoutOptions.Center, + VerticalOptions = LayoutOptions.End, + Text = "Enable tile request delay", + Command = new Command(ToggleTileRequestDelay) + }), + }, + }; + + map.MapReady += Map_MapReady; + map.MapLoaded += Map_MapLoaded; + } + + private void ToggleTileRequestDelay(object obj) + { + isTileRequestDelayEnabled = !isTileRequestDelayEnabled; + + map.MapboxController.SetSourcePropertyFor( + sourceId, + "tile-requests-delay", + isTileRequestDelayEnabled ? 5000 : 0, + (error) => + { + if (error is not null) + { + System.Diagnostics.Debug.WriteLine($"ERR {nameof(ToggleTileRequestDelay)}: ${error}"); + } + }); + + toggleTitleRequestButton.Text = isTileRequestDelayEnabled + ? @"Disable tile request delay" + : @"Enable tile request delay"; + } + + + + public void ApplyQueryAttributes(IDictionary query) + { + info = query["example"] as IExampleInfo; + + Title = info?.Title; + } + + private void Map_MapReady(object sender, EventArgs e) + { + var centerLocation = new MapPosition(40, -74.5); + var cameraOptions = new CameraOptions + { + Center = centerLocation, + Zoom = 2, + }; + + map.CameraOptions = cameraOptions; + map.MapboxStyle = MapboxStyle.SATELLITE; + } + + private void Map_MapLoaded(object sender, EventArgs e) + { + // Setup Styles, Annotations, etc here + // This URL points to raster tiles from OpenStreetMap + string sourceUrl = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"; + + // Create a `RasterSource` and set the source's `tiles` to the Stamen watercolor raster tiles. + var rasterSource = new RasterSource(id: sourceId) + { + Tiles = [sourceUrl], + + // Specify the tile size for the `RasterSource`. + TileSize = 256, + }; + + // Specify that the layer should use the source with the ID `raster-source`. This ID will be + // assigned to the `RasterSource` when it is added to the style. + var rasterLayer = new RasterLayer(id: "raster-layer", sourceId); + + map.Sources = [rasterSource]; + map.Layers = [rasterLayer]; + } +} \ No newline at end of file diff --git a/src/qs/MapboxMauiQs/Examples/Styles/73.RasterTileSource/RasterTileSourceExampleInfo.cs b/src/qs/MapboxMauiQs/Examples/Styles/73.RasterTileSource/RasterTileSourceExampleInfo.cs new file mode 100644 index 0000000..837bb30 --- /dev/null +++ b/src/qs/MapboxMauiQs/Examples/Styles/73.RasterTileSource/RasterTileSourceExampleInfo.cs @@ -0,0 +1,10 @@ +namespace MapboxMauiQs; + +class RasterTileSourceExampleInfo : IExampleInfo +{ + public string Group => "Styles"; + public string Title => "Add a raster tile source"; + public string Subtitle => "Add third-party raster tiles to a map."; + public string PageRoute => typeof(RasterTileSourceExample).FullName; + public int Index => 73; +} \ No newline at end of file diff --git a/src/qs/MapboxMauiQs/MapboxMauiQs.csproj b/src/qs/MapboxMauiQs/MapboxMauiQs.csproj index 9c6256b..939f779 100644 --- a/src/qs/MapboxMauiQs/MapboxMauiQs.csproj +++ b/src/qs/MapboxMauiQs/MapboxMauiQs.csproj @@ -106,7 +106,7 @@ - + diff --git a/tools/generator/index.js b/tools/generator/index.js index 650d59e..f39cd8c 100644 --- a/tools/generator/index.js +++ b/tools/generator/index.js @@ -31,151 +31,152 @@ const swift2CsTypeMapping = { // swiftProperties2CsInterfaceProperties() -// generateLayerProperties('LineLayerKey'); +// generateLayerProperties('RasterLayerKey'); +generateSourceProperties('RasterSourceKey') var commonTypeToConversionNameMapping = { 'bool': 'boolean', }; // generateSourcePropertiesObjc('Vector'); -// generateLayerPropertiesObjc - -function generateLayerPropertiesObjc(layerName) { - var topLines = `// This file is generated. -import Foundation -import MapboxMaps - -@objc open class TMB${layerName}Layer: TMBLayer { - private var _self: ${layerName}Layer { - get { - return rawValue as! ${layerName}Layer - } - set { - rawValue = newValue - } - } +// generateLayerPropertiesObjc(''); + +// function generateLayerPropertiesObjc(layerName) { +// var topLines = `// This file is generated. +// import Foundation +// import MapboxMaps + +// @objc open class TMB${layerName}Layer: TMBLayer { +// private var _self: ${layerName}Layer { +// get { +// return rawValue as! ${layerName}Layer +// } +// set { +// rawValue = newValue +// } +// } - @objc public init(id: String = UUID().uuidString) { - super.init(${layerName}Layer(id: id)) +// @objc public init(id: String = UUID().uuidString) { +// super.init(${layerName}Layer(id: id)) - self.visibility = TMBValue(constant: TMBVisibility.visible) - } +// self.visibility = TMBValue(constant: TMBVisibility.visible) +// } - ` +// ` - var transformed = lines.map(item => { - let isPropertyLine = /^\s+public/.test(item); - if (!isPropertyLine) { - return item; - } +// var transformed = lines.map(item => { +// let isPropertyLine = /^\s+public/.test(item); +// if (!isPropertyLine) { +// return item; +// } - var matches = /(\w+): Value<(\[?\w+\]?)>/.exec(item); +// var matches = /(\w+): Value<(\[?\w+\]?)>/.exec(item); - if (!matches) { - matches = /(\w+): (\[?\w+\]?)/.exec(item); - } - - var propName = matches[1]; - var propType = matches[2]; - var conversionName = propType.substring(0,1).toLowerCase() + propType.substring(1); - if (commonTypeToConversionNameMapping[conversionName]) { - conversionName = commonTypeToConversionNameMapping[conversionName]; - } else if (/^\[/.test(propType)) { - conversionName = 'arrayOf' + propType.replace(/\[|\]/img, ''); - } - - if (propType == 'StyleTransition') { - return ` @objc public var ${propName} : TMBStyleTransition? { - get { - return _self.${propName}?.objcValue() - } - set { - _self.${propName} = newValue?.rawValue - } - }`; - } +// if (!matches) { +// matches = /(\w+): (\[?\w+\]?)/.exec(item); +// } + +// var propName = matches[1]; +// var propType = matches[2]; +// var conversionName = propType.substring(0,1).toLowerCase() + propType.substring(1); +// if (commonTypeToConversionNameMapping[conversionName]) { +// conversionName = commonTypeToConversionNameMapping[conversionName]; +// } else if (/^\[/.test(propType)) { +// conversionName = 'arrayOf' + propType.replace(/\[|\]/img, ''); +// } + +// if (propType == 'StyleTransition') { +// return ` @objc public var ${propName} : TMBStyleTransition? { +// get { +// return _self.${propName}?.objcValue() +// } +// set { +// _self.${propName} = newValue?.rawValue +// } +// }`; +// } - return ` @objc public var ${propName} : TMBValue? { - get { - return TMBValue.fromSwiftValue(_self.${propName}) - } - set { - _self.${propName} = newValue?.${conversionName}() - } - }`; - }); +// return ` @objc public var ${propName} : TMBValue? { +// get { +// return TMBValue.fromSwiftValue(_self.${propName}) +// } +// set { +// _self.${propName} = newValue?.${conversionName}() +// } +// }`; +// }); - fs.writeFileSync('output.txt', topLines + transformed.join('\n') + '\n}'); -} - -function generateSourcePropertiesObjc(srcName) { - var nsnumberTypes = { - 'Bool': 'boolValue', - 'Double': 'doubleValue', - } - var nsnumberConversions = { - 'Bool': 'asNumber()', - 'Int': 'asNumber()', - 'Double': 'NSNumber', - } - var nsvalueTypes = { - - } - var topLines = `// This file is generated. -import Foundation -import MapboxMaps - -@objc open class TMB${srcName}Source: TMBSource { - private var _self: ${srcName}Source { - get { - return rawValue as! ${srcName}Source - } - set { - rawValue = newValue - } - } +// fs.writeFileSync('output.txt', topLines + transformed.join('\n') + '\n}'); +// } + +// function generateSourcePropertiesObjc(srcName) { +// var nsnumberTypes = { +// 'Bool': 'boolValue', +// 'Double': 'doubleValue', +// } +// var nsnumberConversions = { +// 'Bool': 'asNumber()', +// 'Int': 'asNumber()', +// 'Double': 'NSNumber', +// } +// var nsvalueTypes = { + +// } +// var topLines = `// This file is generated. +// import Foundation +// import MapboxMaps + +// @objc open class TMB${srcName}Source: TMBSource { +// private var _self: ${srcName}Source { +// get { +// return rawValue as! ${srcName}Source +// } +// set { +// rawValue = newValue +// } +// } - @objc public init() { - super.init(${srcName}Source()) - } +// @objc public init() { +// super.init(${srcName}Source()) +// } - `; +// `; - var transformed = lines.map(item => { - let isPropertyLine = /^\s+public/.test(item); - if (!isPropertyLine) { - return item; - } +// var transformed = lines.map(item => { +// let isPropertyLine = /^\s+public/.test(item); +// if (!isPropertyLine) { +// return item; +// } - var matches = /(\w+): ([\[]{0,2}\w+[\]]{0,2})(\??)/.exec(item); - - var propName = matches[1]; - var propType = matches[2]; - let nullable = matches[3]; - var conversionName = propType.trim('?') - if (nsnumberTypes[conversionName]) { - return ` @objc public var ${propName} : NSNumber${nullable} { - get { - return _self.${propName}${nullable}.${nsnumberConversions[propType]} - } - set { - _self.${propName} = newValue${nullable}.${nsnumberTypes[conversionName]} - } - }`; - } - - return ` @objc public var ${propName} : ${conversionName}? { - get { - return _self.${propName} - } - set { - _self.${propName} = newValue - } - }`; - }); +// var matches = /(\w+): ([\[]{0,2}\w+[\]]{0,2})(\??)/.exec(item); + +// var propName = matches[1]; +// var propType = matches[2]; +// let nullable = matches[3]; +// var conversionName = propType.trim('?') +// if (nsnumberTypes[conversionName]) { +// return ` @objc public var ${propName} : NSNumber${nullable} { +// get { +// return _self.${propName}${nullable}.${nsnumberConversions[propType]} +// } +// set { +// _self.${propName} = newValue${nullable}.${nsnumberTypes[conversionName]} +// } +// }`; +// } + +// return ` @objc public var ${propName} : ${conversionName}? { +// get { +// return _self.${propName} +// } +// set { +// _self.${propName} = newValue +// } +// }`; +// }); - fs.writeFileSync('output.txt', topLines + transformed.join('\n') + '\n}'); -} +// fs.writeFileSync('output.txt', topLines + transformed.join('\n') + '\n}'); +// } function generateAndroidNamedString() {