Skip to content

Commit 70a1b3c

Browse files
committed
feat(google-maps): Implemented map feature functions (i.e. GeoJSON support)
1 parent 262a5e4 commit 70a1b3c

12 files changed

Lines changed: 856 additions & 5 deletions

File tree

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ Vue({
373373
* [`removeCircles(...)`](#removecircles)
374374
* [`addPolylines(...)`](#addpolylines)
375375
* [`removePolylines(...)`](#removepolylines)
376+
* [`addFeatures(...)`](#addfeatures)
377+
* [`getFeatureBounds(...)`](#getfeaturebounds)
378+
* [`removeFeature(...)`](#removefeature)
376379
* [`destroy()`](#destroy)
377380
* [`setCamera(...)`](#setcamera)
378381
* [`getMapType()`](#getmaptype)
@@ -633,6 +636,52 @@ removePolylines(ids: string[]) => Promise<void>
633636
--------------------
634637

635638

639+
### addFeatures(...)
640+
641+
```typescript
642+
addFeatures(type: FeatureType, data: any, idPropertyName?: string | undefined, styles?: FeatureStyles | undefined) => Promise<string[]>
643+
```
644+
645+
| Param | Type |
646+
| -------------------- | ------------------------------------------------------- |
647+
| **`type`** | <code><a href="#featuretype">FeatureType</a></code> |
648+
| **`data`** | <code>any</code> |
649+
| **`idPropertyName`** | <code>string</code> |
650+
| **`styles`** | <code><a href="#featurestyles">FeatureStyles</a></code> |
651+
652+
**Returns:** <code>Promise&lt;string[]&gt;</code>
653+
654+
--------------------
655+
656+
657+
### getFeatureBounds(...)
658+
659+
```typescript
660+
getFeatureBounds(featureId: string) => Promise<LatLngBounds>
661+
```
662+
663+
| Param | Type |
664+
| --------------- | ------------------- |
665+
| **`featureId`** | <code>string</code> |
666+
667+
**Returns:** <code>Promise&lt;LatLngBounds&gt;</code>
668+
669+
--------------------
670+
671+
672+
### removeFeature(...)
673+
674+
```typescript
675+
removeFeature(featureId: string) => Promise<void>
676+
```
677+
678+
| Param | Type |
679+
| --------------- | ------------------- |
680+
| **`featureId`** | <code>string</code> |
681+
682+
--------------------
683+
684+
636685
### destroy()
637686

638687
```typescript
@@ -1158,6 +1207,11 @@ Describes the style for some region of a polyline.
11581207
| **`segments`** | <code>number</code> | The length of this span in number of segments. |
11591208

11601209

1210+
#### FeatureStyles
1211+
1212+
Feature styles, identified by the feature id
1213+
1214+
11611215
#### CameraConfig
11621216

11631217
Configuration properties for a Google Map Camera
@@ -1296,6 +1350,14 @@ Supports markers of either "legacy" or "advanced" types.
12961350
### Enums
12971351

12981352

1353+
#### FeatureType
1354+
1355+
| Members | Value | Description |
1356+
| ------------- | ---------------------- | ----------- |
1357+
| **`Default`** | <code>'Default'</code> | Default |
1358+
| **`GeoJSON`** | <code>'GeoJSON'</code> | GeoJSON |
1359+
1360+
12991361
#### MapType
13001362

13011363
| Members | Value | Description |

plugin/README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ Vue({
373373
* [`removeCircles(...)`](#removecircles)
374374
* [`addPolylines(...)`](#addpolylines)
375375
* [`removePolylines(...)`](#removepolylines)
376+
* [`addFeatures(...)`](#addfeatures)
377+
* [`getFeatureBounds(...)`](#getfeaturebounds)
378+
* [`removeFeature(...)`](#removefeature)
376379
* [`destroy()`](#destroy)
377380
* [`setCamera(...)`](#setcamera)
378381
* [`getMapType()`](#getmaptype)
@@ -633,6 +636,52 @@ removePolylines(ids: string[]) => Promise<void>
633636
--------------------
634637

635638

639+
### addFeatures(...)
640+
641+
```typescript
642+
addFeatures(type: FeatureType, data: any, idPropertyName?: string | undefined, styles?: FeatureStyles | undefined) => Promise<string[]>
643+
```
644+
645+
| Param | Type |
646+
| -------------------- | ------------------------------------------------------- |
647+
| **`type`** | <code><a href="#featuretype">FeatureType</a></code> |
648+
| **`data`** | <code>any</code> |
649+
| **`idPropertyName`** | <code>string</code> |
650+
| **`styles`** | <code><a href="#featurestyles">FeatureStyles</a></code> |
651+
652+
**Returns:** <code>Promise&lt;string[]&gt;</code>
653+
654+
--------------------
655+
656+
657+
### getFeatureBounds(...)
658+
659+
```typescript
660+
getFeatureBounds(featureId: string) => Promise<LatLngBounds>
661+
```
662+
663+
| Param | Type |
664+
| --------------- | ------------------- |
665+
| **`featureId`** | <code>string</code> |
666+
667+
**Returns:** <code>Promise&lt;LatLngBounds&gt;</code>
668+
669+
--------------------
670+
671+
672+
### removeFeature(...)
673+
674+
```typescript
675+
removeFeature(featureId: string) => Promise<void>
676+
```
677+
678+
| Param | Type |
679+
| --------------- | ------------------- |
680+
| **`featureId`** | <code>string</code> |
681+
682+
--------------------
683+
684+
636685
### destroy()
637686

638687
```typescript
@@ -1158,6 +1207,11 @@ Describes the style for some region of a polyline.
11581207
| **`segments`** | <code>number</code> | The length of this span in number of segments. |
11591208

11601209

1210+
#### FeatureStyles
1211+
1212+
Feature styles, identified by the feature id
1213+
1214+
11611215
#### CameraConfig
11621216

11631217
Configuration properties for a Google Map Camera
@@ -1296,6 +1350,14 @@ Supports markers of either "legacy" or "advanced" types.
12961350
### Enums
12971351

12981352

1353+
#### FeatureType
1354+
1355+
| Members | Value | Description |
1356+
| ------------- | ---------------------- | ----------- |
1357+
| **`Default`** | <code>'Default'</code> | Default |
1358+
| **`GeoJSON`** | <code>'GeoJSON'</code> | GeoJSON |
1359+
1360+
12991361
#### MapType
13001362

13011363
| Members | Value | Description |

plugin/android/src/main/java/com/capacitorjs/plugins/googlemaps/CapacitorGoogleMap.kt

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,19 @@ import com.google.android.gms.maps.GoogleMap.*
1616
import com.google.android.gms.maps.model.*
1717
import com.google.maps.android.clustering.Cluster
1818
import com.google.maps.android.clustering.ClusterManager
19+
import com.google.maps.android.data.Feature
20+
import com.google.maps.android.data.geojson.GeoJsonFeature
21+
import com.google.maps.android.data.geojson.GeoJsonGeometryCollection
22+
import com.google.maps.android.data.geojson.GeoJsonLayer
23+
import com.google.maps.android.data.geojson.GeoJsonLineString
24+
import com.google.maps.android.data.geojson.GeoJsonMultiLineString
25+
import com.google.maps.android.data.geojson.GeoJsonMultiPoint
26+
import com.google.maps.android.data.geojson.GeoJsonMultiPolygon
27+
import com.google.maps.android.data.geojson.GeoJsonPoint
28+
import com.google.maps.android.data.geojson.GeoJsonPolygon
1929
import kotlinx.coroutines.*
2030
import kotlinx.coroutines.channels.Channel
31+
import org.json.JSONObject
2132
import java.io.InputStream
2233
import java.net.URL
2334

@@ -45,7 +56,8 @@ class CapacitorGoogleMap(
4556
private val tileOverlays = HashMap<String, CapacitorGoogleMapTileOverlay>()
4657
private val polygons = HashMap<String, CapacitorGoogleMapsPolygon>()
4758
private val circles = HashMap<String, CapacitorGoogleMapsCircle>()
48-
private val polylines = HashMap<String, CapacitorGoogleMapPolyline>()
59+
private val polylines = HashMap<String, CapacitorGoogleMapPolyline>()
60+
private val featureLayers = HashMap<String, CapacitorGoogleMapsFeatureLayer>()
4961
private val markerIcons = HashMap<String, Bitmap>()
5062
private var clusterManager: ClusterManager<CapacitorGoogleMapMarker>? = null
5163

@@ -415,6 +427,78 @@ class CapacitorGoogleMap(
415427
}
416428
}
417429

430+
fun addFeatures(type: String, data: JSONObject, idPropertyName: String?, styles: JSONObject?, callback: (ids: Result<List<String>>) -> Unit) {
431+
try {
432+
googleMap ?: throw GoogleMapNotAvailable()
433+
val featureIds: MutableList<String> = mutableListOf()
434+
435+
CoroutineScope(Dispatchers.Main).launch {
436+
if (type == "GeoJSON") {
437+
val tempLayer = GeoJsonLayer(null, data)
438+
tempLayer.features.forEach {
439+
try {
440+
val layer = GeoJsonLayer(googleMap, JSONObject())
441+
val featureLayer = CapacitorGoogleMapsFeatureLayer(layer, it, idPropertyName, styles)
442+
layer.addLayerToMap()
443+
if (id != null) {
444+
featureIds.add(id)
445+
featureLayers[id] = featureLayer
446+
}
447+
callback(Result.success(featureIds))
448+
} catch (e: Exception) {
449+
callback(Result.failure(e))
450+
}
451+
}
452+
} else {
453+
callback(Result.failure(InvalidArgumentsError("addFeatures: not supported for this feature type")))
454+
}
455+
}
456+
} catch (e: GoogleMapsError) {
457+
callback(Result.failure(e))
458+
}
459+
}
460+
461+
fun getFeatureBounds(featureId: String, callback: (bounds: Result<LatLngBounds?>) -> Unit) {
462+
try {
463+
CoroutineScope(Dispatchers.Main).launch {
464+
val featurelayer = featureLayers[featureId]
465+
var feature: Feature? = null;
466+
featurelayer?.layer?.features?.forEach lit@ {
467+
if (it.id == featurelayer.id) {
468+
feature = it
469+
return@lit
470+
}
471+
}
472+
if (feature != null) {
473+
try {
474+
(feature as GeoJsonFeature).let {
475+
callback(Result.success(it.boundingBoxFromGeometry()))
476+
}
477+
} catch (e: Exception) {
478+
callback(Result.failure(InvalidArgumentsError("getFeatureBounds: not supported for this feature type")))
479+
}
480+
} else {
481+
callback(Result.failure(InvalidArgumentsError("Could not find feature for feature id $featureId")))
482+
}
483+
}
484+
} catch(e: Exception) {
485+
callback(Result.failure(InvalidArgumentsError("Could not find feature layer")))
486+
}
487+
}
488+
489+
fun removeFeature(featureId: String, callback: (error: GoogleMapsError?) -> Unit) {
490+
CoroutineScope(Dispatchers.Main).launch {
491+
val featurelayer = featureLayers[featureId]
492+
if (featurelayer != null) {
493+
featurelayer.layer?.removeLayerFromMap()
494+
featureLayers.remove(featureId)
495+
callback(null)
496+
} else {
497+
callback(InvalidArgumentsError("Could not find feature for feature id $featureId"))
498+
}
499+
}
500+
}
501+
418502
private fun setClusterManagerRenderer(minClusterSize: Int?) {
419503
clusterManager?.renderer = CapacitorClusterManagerRenderer(
420504
delegate.bridge.context,
@@ -1032,6 +1116,52 @@ class CapacitorGoogleMap(
10321116
return data
10331117
}
10341118

1119+
private fun GeoJsonFeature.boundingBoxFromGeometry(): LatLngBounds? {
1120+
val coordinates: MutableList<LatLng> = ArrayList()
1121+
1122+
if (this.hasGeometry()) {
1123+
when (geometry.geometryType) {
1124+
"Point" -> coordinates.add((geometry as GeoJsonPoint).coordinates)
1125+
"MultiPoint" -> {
1126+
val points = (geometry as GeoJsonMultiPoint).points
1127+
for (point in points) {
1128+
coordinates.add(point.coordinates)
1129+
}
1130+
}
1131+
1132+
"LineString" -> coordinates.addAll((geometry as GeoJsonLineString).coordinates)
1133+
"MultiLineString" -> {
1134+
val lines = (geometry as GeoJsonMultiLineString).lineStrings
1135+
for (line in lines) {
1136+
coordinates.addAll(line.coordinates)
1137+
}
1138+
}
1139+
1140+
"Polygon" -> {
1141+
val lists = (geometry as GeoJsonPolygon).coordinates
1142+
for (list in lists) {
1143+
coordinates.addAll(list)
1144+
}
1145+
}
1146+
1147+
"MultiPolygon" -> {
1148+
val polygons = (geometry as GeoJsonMultiPolygon).polygons
1149+
for (polygon in polygons) {
1150+
for (list in polygon.coordinates) {
1151+
coordinates.addAll(list)
1152+
}
1153+
}
1154+
}
1155+
}
1156+
}
1157+
1158+
val builder = LatLngBounds.builder()
1159+
for (latLng in coordinates) {
1160+
builder.include(latLng)
1161+
}
1162+
return builder.build()
1163+
}
1164+
10351165
override fun onMapClick(point: LatLng) {
10361166
val data = JSObject()
10371167
data.put("mapId", this@CapacitorGoogleMap.id)

0 commit comments

Comments
 (0)