Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: more sources and layers #24

Merged
merged 25 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@

### Features

- add `duration` parameter to `flyTo()`
- Add `duration` parameter to `flyTo()`.
- `flyTo()` returns after the animation completes or throws an exception if it
has been cancelled
has been cancelled.
- Add sources and layers to the map programmatically.

### Bug fixes

- fix `jumpTo()` never returns
- Fix `jumpTo()` never returns

## 0.0.1+1

- fix urls to website and embedded screenshots
- remove unused `plugin_platform_interface` dependency
- Fix urls to website and embedded screenshots
- Remove unused `plugin_platform_interface` dependency

## 0.0.1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,50 @@ import MapCamera
import MapLibreFlutterApi
import MapLibreHostApi
import MapOptions
import RasterDemEncoding
import ScreenLocation
import TileScheme
import android.content.Context
import android.graphics.PointF
import android.view.View
import android.widget.FrameLayout
import androidx.lifecycle.DefaultLifecycleObserver
import com.google.gson.Gson
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.platform.PlatformView
import org.maplibre.android.MapLibre
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.camera.CameraUpdateFactory
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.geometry.LatLngQuad
import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.maps.MapLibreMapOptions
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.OnMapReadyCallback
import org.maplibre.android.maps.Style
import org.maplibre.android.style.expressions.Expression
import org.maplibre.android.style.layers.BackgroundLayer
import org.maplibre.android.style.layers.CircleLayer
import org.maplibre.android.style.layers.FillExtrusionLayer
import org.maplibre.android.style.layers.FillLayer
import org.maplibre.android.style.layers.HeatmapLayer
import org.maplibre.android.style.layers.HillshadeLayer
import org.maplibre.android.style.layers.LineLayer
import org.maplibre.android.style.layers.PaintPropertyValue
import org.maplibre.android.style.layers.PropertyValue
import org.maplibre.android.style.layers.RasterLayer
import org.maplibre.android.style.layers.SymbolLayer
import org.maplibre.android.style.sources.GeoJsonOptions
import org.maplibre.android.style.sources.GeoJsonSource
import org.maplibre.android.style.sources.ImageSource
import org.maplibre.android.style.sources.RasterDemSource
import org.maplibre.android.style.sources.RasterSource
import org.maplibre.android.style.sources.TileSet
import org.maplibre.android.style.sources.VectorSource
import java.net.URI
import kotlin.coroutines.cancellation.CancellationException


class MapLibreMapController(
viewId: Int,
private val context: Context,
Expand Down Expand Up @@ -152,6 +174,195 @@ class MapLibreMapController(
callback(Result.success(LngLat(latLng.longitude, latLng.latitude)))
}

private val gson = Gson()
private fun parseProperties(entries: Map<String, Any>): Array<PropertyValue<*>> {
return entries.map { entry ->
// println("${entry.key}; ${entry.value::class.java.typeName}; ${entry.value}")
when (entry.value) {
is ArrayList<*> -> {
val value = entry.value as ArrayList<*>
val json = gson.toJsonTree(value)
val expression = Expression.Converter.convert(json)
PaintPropertyValue(entry.key, expression)
}

else -> PaintPropertyValue(entry.key, entry.value)
}
}.toTypedArray()
}

override fun addFillLayer(
id: String,
sourceId: String,
layout: Map<String, Any>,
paint: Map<String, Any>,
belowLayerId: String?,
callback: (Result<Unit>) -> Unit
) {
val layer = FillLayer(id, sourceId)
layer.setProperties(*parseProperties(paint), *parseProperties(layout))
if (belowLayerId == null) {
mapLibreMap.style?.addLayer(layer)
} else {
mapLibreMap.style?.addLayerBelow(layer, belowLayerId)
}
callback(Result.success(Unit))
}

override fun addCircleLayer(
id: String,
sourceId: String,
layout: Map<String, Any>,
paint: Map<String, Any>,
belowLayerId: String?,
callback: (Result<Unit>) -> Unit
) {
val layer = CircleLayer(id, sourceId)
layer.setProperties(*parseProperties(paint), *parseProperties(layout))
if (belowLayerId == null) {
mapLibreMap.style?.addLayer(layer)
} else {
mapLibreMap.style?.addLayerBelow(layer, belowLayerId)
}
callback(Result.success(Unit))
}

override fun addBackgroundLayer(
id: String,
layout: Map<String, Any>,
paint: Map<String, Any>,
belowLayerId: String?,
callback: (Result<Unit>) -> Unit
) {
val layer = BackgroundLayer(id)
layer.setProperties(*parseProperties(paint), *parseProperties(layout))
if (belowLayerId == null) {
mapLibreMap.style?.addLayer(layer)
} else {
mapLibreMap.style?.addLayerBelow(layer, belowLayerId)
}
callback(Result.success(Unit))
}

override fun addFillExtrusionLayer(
id: String,
sourceId: String,
layout: Map<String, Any>,
paint: Map<String, Any>,
belowLayerId: String?,
callback: (Result<Unit>) -> Unit
) {
val layer = FillExtrusionLayer(id, sourceId)
layer.setProperties(*parseProperties(paint), *parseProperties(layout))
if (belowLayerId == null) {
mapLibreMap.style?.addLayer(layer)
} else {
mapLibreMap.style?.addLayerBelow(layer, belowLayerId)
}
callback(Result.success(Unit))
}

override fun addHeatmapLayer(
id: String,
sourceId: String,
layout: Map<String, Any>,
paint: Map<String, Any>,
belowLayerId: String?,
callback: (Result<Unit>) -> Unit
) {
val layer = HeatmapLayer(id, sourceId)
layer.setProperties(*parseProperties(paint), *parseProperties(layout))
if (belowLayerId == null) {
mapLibreMap.style?.addLayer(layer)
} else {
mapLibreMap.style?.addLayerBelow(layer, belowLayerId)
}
callback(Result.success(Unit))
}

override fun addHillshadeLayer(
id: String,
sourceId: String,
layout: Map<String, Any>,
paint: Map<String, Any>,
belowLayerId: String?,
callback: (Result<Unit>) -> Unit
) {
val layer = HillshadeLayer(id, sourceId)
layer.setProperties(*parseProperties(paint), *parseProperties(layout))
if (belowLayerId == null) {
mapLibreMap.style?.addLayer(layer)
} else {
mapLibreMap.style?.addLayerBelow(layer, belowLayerId)
}
callback(Result.success(Unit))
}

override fun addLineLayer(
id: String,
sourceId: String,
layout: Map<String, Any>,
paint: Map<String, Any>,
belowLayerId: String?,
callback: (Result<Unit>) -> Unit
) {
val layer = LineLayer(id, sourceId)
layer.setProperties(*parseProperties(paint), *parseProperties(layout))
if (belowLayerId == null) {
mapLibreMap.style?.addLayer(layer)
} else {
mapLibreMap.style?.addLayerBelow(layer, belowLayerId)
}
callback(Result.success(Unit))
}

override fun addRasterLayer(
id: String,
sourceId: String,
layout: Map<String, Any>,
paint: Map<String, Any>,
belowLayerId: String?,
callback: (Result<Unit>) -> Unit
) {
val layer = RasterLayer(id, sourceId)
// layer.setProperties(*parseProperties(paint), *parseProperties(layout))
if (belowLayerId == null) {
mapLibreMap.style?.addLayer(layer)
} else {
mapLibreMap.style?.addLayerBelow(layer, belowLayerId)
}
callback(Result.success(Unit))
}

override fun addSymbolLayer(
id: String,
sourceId: String,
layout: Map<String, Any>,
paint: Map<String, Any>,
belowLayerId: String?,
callback: (Result<Unit>) -> Unit
) {
val layer = SymbolLayer(id, sourceId)
layer.setProperties(*parseProperties(paint), *parseProperties(layout))
if (belowLayerId == null) {
mapLibreMap.style?.addLayer(layer)
} else {
mapLibreMap.style?.addLayerBelow(layer, belowLayerId)
}
callback(Result.success(Unit))
}

override fun removeLayer(id: String, callback: (Result<Unit>) -> Unit) {
mapLibreMap.style?.removeLayer(id)
callback(Result.success(Unit))

}

override fun removeSource(id: String, callback: (Result<Unit>) -> Unit) {
mapLibreMap.style?.removeSource(id)
callback(Result.success(Unit))
}

override fun getCamera(callback: (Result<MapCamera>) -> Unit) {
val position = mapLibreMap.cameraPosition
val target = mapLibreMap.cameraPosition.target!!
Expand All @@ -171,26 +382,107 @@ class MapLibreMapController(
callback(Result.success(lngLatBounds))
}

override fun addFillLayer(id: String, sourceId: String, callback: (Result<Unit>) -> Unit) {
mapLibreMap.style?.addLayer(FillLayer(id, sourceId))
override fun addGeoJsonSource(
id: String,
data: String,
callback: (Result<Unit>) -> Unit
) {
val options = GeoJsonOptions()
val source = if (data.first() == '{') {
GeoJsonSource(id, data, options)
} else {
GeoJsonSource(id, URI(data), options)
}
mapLibreMap.style?.addSource(source)
callback(Result.success(Unit))
}

override fun addCircleLayer(
override fun addImageSource(
id: String,
sourceId: String,
url: String,
coordinates: List<LngLat>,
callback: (Result<Unit>) -> Unit
) {
mapLibreMap.style?.addLayer(CircleLayer(id, sourceId))
val quad = LatLngQuad(
LatLng(coordinates[0].lat, coordinates[0].lng),
LatLng(coordinates[0].lat, coordinates[0].lng),
LatLng(coordinates[0].lat, coordinates[0].lng),
LatLng(coordinates[0].lat, coordinates[0].lng)
)
val source = ImageSource(id, quad, URI(url))
mapLibreMap.style?.addSource(source)
callback(Result.success(Unit))
}

override fun addGeoJsonSource(
override fun addRasterSource(
id: String,
data: String,
url: String?,
tiles: List<String>?,
bounds: List<Double>,
minZoom: Double,
maxZoom: Double,
tileSize: Long,
scheme: TileScheme,
attribution: String?,
volatile: Boolean,
callback: (Result<Unit>) -> Unit
) {
val source = if (url == null) {
// TODO improve this
val tileSet = TileSet("{}", tiles!!.first())
tileSet.maxZoom = maxZoom.toFloat()
tileSet.minZoom = minZoom.toFloat()
RasterSource(id, tileSet, tileSize.toInt())
} else {
RasterSource(id, url, tileSize.toInt())
}
source.isVolatile = volatile
// TODO apply other properties
mapLibreMap.style?.addSource(source)
callback(Result.success(Unit))
}

override fun addRasterDemSource(
id: String,
url: String?,
tiles: List<String>?,
bounds: List<Double>,
minZoom: Double,
maxZoom: Double,
tileSize: Long,
attribution: String?,
encoding: RasterDemEncoding,
volatile: Boolean,
redFactor: Double,
blueFactor: Double,
greenFactor: Double,
baseShift: Double,
callback: (Result<Unit>) -> Unit
) {
val source = RasterDemSource(id, url, tileSize.toInt())
source.isVolatile = volatile
// TODO apply other properties
mapLibreMap.style?.addSource(source)
callback(Result.success(Unit))
}

override fun addVectorSource(
id: String,
url: String?,
tiles: List<String>?,
bounds: List<Double>,
scheme: TileScheme,
minZoom: Double,
maxZoom: Double,
attribution: String?,
volatile: Boolean,
sourceLayer: String?,
callback: (Result<Unit>) -> Unit
) {
mapLibreMap.style?.addSource(GeoJsonSource(id, data))
val source = VectorSource(id, url)
source.isVolatile = volatile
// TODO apply other properties
mapLibreMap.style?.addSource(source)
callback(Result.success(Unit))
}

Expand Down
Loading