mapbox / mapbox-maps-android

Interactive, thoroughly customizable maps in native Android powered by vector tiles and OpenGL.
https://www.mapbox.com/mobile-maps-sdk
Other
466 stars 131 forks source link

Heatmap is not visible after adding it #2009

Closed PavlosTze closed 1 year ago

PavlosTze commented 1 year ago

Environment

Observed behavior and steps to reproduce

I am adding heatmap via geojson and it isn't added anymore on the first try. I am using the following code, the else part runs on the first onViewCreated of this fragment, whereas the if runs on every next run.

On 10.10.0 this was running smoothly. On 10.11.0 on the first onViewCreated the heatmap isn't added in the map and it isn't visible. On every next run it is added correctly.

                        if (mapStyle?.styleSourceExists(HEATMAP_SOURCE_ID) == true) {
                            source.data?.let {
                                (mapStyle.getSource(HEATMAP_SOURCE_ID) as GeoJsonSource).data(it)
                            }
                        } else {
                            mapStyle?.addSource(source)
                            mapStyle?.addLayerAbove(model.getHeatMapLayer(), "waterway-label")
                        }

Expected behavior

Have the heatmap visible at all times.

Notes / preliminary analysis

Additional links and references

PavlosTze commented 1 year ago

I also get this error on console at 10.11.0 which is not happening in 10.10.x versions.

W/Mapbox: [maps-android\GeoJsonSource]: GeoJsonSource (id=heatmap) was not able to set data with feature(), featureCollection() or geometry() as there is no Style object.

kiryldz commented 1 year ago

@PavlosTze I tried modifying our test activity in a way (as I understood) it's not working for you and I could see the data as expected:

diff --git a/mapbox-maps-android/app/src/main/java/com/mapbox/maps/testapp/examples/globe/HeatmapLayerGlobeActivity.kt b/mapbox-maps-android/app/src/main/java/com/mapbox/maps/testapp/examples/globe/HeatmapLayerGlobeActivity.kt
index bf589756..461f638c 100644
--- a/mapbox-maps-android/app/src/main/java/com/mapbox/maps/testapp/examples/globe/HeatmapLayerGlobeActivity.kt
+++ b/mapbox-maps-android/app/src/main/java/com/mapbox/maps/testapp/examples/globe/HeatmapLayerGlobeActivity.kt
@@ -51,7 +51,8 @@ class HeatmapLayerGlobeActivity : AppCompatActivity() {

   private fun createEarthquakeSource(): GeoJsonSource {
     return geoJsonSource(EARTHQUAKE_SOURCE_ID) {
-      url(EARTHQUAKE_SOURCE_URL)
+//      url(EARTHQUAKE_SOURCE_URL)
+      data(EARTHQUAKE_DATA)
     }
   }

@@ -251,6 +252,63 @@ class HeatmapLayerGlobeActivity : AppCompatActivity() {
   companion object {
     private const val EARTHQUAKE_SOURCE_URL =
       "https://www.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson"
+    private val EARTHQUAKE_DATA = """
+      {
+      "type": "FeatureCollection",
+      "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
+      "features": [
+      { "type": "Feature", "properties": { "id": "ak16994521", "mag": 2.3, "time": 1507425650893, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -151.5129, 63.1016, 0.0 ] } },
+      { "type": "Feature", "properties": { "id": "ak16994519", "mag": 1.7, "time": 1507425289659, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -150.4048, 63.1224, 105.5 ] } },
+      { "type": "Feature", "properties": { "id": "ak16994517", "mag": 1.6, "time": 1507424832518, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -151.3597, 63.0781, 0.0 ] } },
+      { "type": "Feature", "properties": { "id": "ci38021336", "mag": 1.42, "time": 1507423898710, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -118.497, 34.299667, 7.64 ] } },
+      { "type": "Feature", "properties": { "id": "us2000b2nn", "mag": 4.2, "time": 1507422626990, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -87.6901, 12.0623, 46.41 ] } },
+      { "type": "Feature", "properties": { "id": "ak16994510", "mag": 1.6, "time": 1507422449194, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -151.5053, 63.0719, 0.0 ] } },
+      { "type": "Feature", "properties": { "id": "pr2017251000", "mag": 3.73, "time": 1504847643290, "felt": 2, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -68.2905, 19.3283, 52.0 ] } },
+      { "type": "Feature", "properties": { "id": "us2000arqw", "mag": 4.8, "time": 1504847625230, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -93.8789, 15.4565, 61.03 ] } },
+      { "type": "Feature", "properties": { "id": "us2000arqv", "mag": 5.0, "time": 1504847479550, "felt": null, "tsunami": 1 }, "geometry": { "type": "Point", "coordinates": [ 128.5159, 2.4289, 235.24 ] } },
+      { "type": "Feature", "properties": { "id": "nc71107629", "mag": 1.35, "time": 1504847454920, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -122.805833, 38.815333, 2.23 ] } },
+      { "type": "Feature", "properties": { "id": "nc72888186", "mag": 1.45, "time": 1504847378350, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -122.766, 38.8255, 0.39 ] } },
+      { "type": "Feature", "properties": { "id": "nc72888181", "mag": 2.28, "time": 1504847361610, "felt": 2, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -122.7555, 38.775667, 0.02 ] } },
+      { "type": "Feature", "properties": { "id": "nc72888171", "mag": 1.83, "time": 1504847323920, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -122.7715, 38.817333, -0.11 ] } },
+      { "type": "Feature", "properties": { "id": "nc71107399", "mag": 1.12, "time": 1504847319000, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -122.771167, 38.819333, -0.31 ] } },
+      { "type": "Feature", "properties": { "id": "nc72888156", "mag": 1.24, "time": 1504847297950, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -122.556, 38.807, 15.91 ] } },
+      { "type": "Feature", "properties": { "id": "nc72888151", "mag": 1.36, "time": 1504847289840, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -122.806, 38.822, 1.41 ] } },
+      { "type": "Feature", "properties": { "id": "nc72888176", "mag": 1.79, "time": 1504847279970, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -122.739667, 38.774, -0.49 ] } },
+      { "type": "Feature", "properties": { "id": "nc72888146", "mag": 2.3, "time": 1504847258860, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -122.817833, 38.815167, 0.34 ] } },
+      { "type": "Feature", "properties": { "id": "nc72888141", "mag": 2.42, "time": 1504847223340, "felt": 2, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -122.800667, 38.83, 1.81 ] } },
+      { "type": "Feature", "properties": { "id": "us2000ahv7", "mag": 5.7, "time": 1504846893100, "felt": 26, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -94.2707, 15.1746, 35.87 ] } },
+      { "type": "Feature", "properties": { "id": "ak16777708", "mag": 1.1, "time": 1504846594461, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -151.0631, 61.4229, 6.8 ] } },
+      { "type": "Feature", "properties": { "id": "mb80252994", "mag": 1.72, "time": 1504846249390, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -112.541833, 46.857167, 12.55 ] } },
+      { "type": "Feature", "properties": { "id": "us2000ahv0", "mag": 8.1, "time": 1504846160000, "felt": 2494, "tsunami": 1 }, "geometry": { "type": "Point", "coordinates": [ -93.9067, 15.0356, 56.67 ] } },
+      { "type": "Feature", "properties": { "id": "us2000ai6a", "mag": 2.5, "time": 1504846040410, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -163.4753, 53.7845, 22.98 ] } },
+      { "type": "Feature", "properties": { "id": "nc72888131", "mag": 1.2, "time": 1504845594500, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -118.8105, 37.463833, -1.37 ] } },
+      { "type": "Feature", "properties": { "id": "ak16821844", "mag": 2.0, "time": 1504845256450, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ 178.621, 51.2706, 13.2 ] } },
+      { "type": "Feature", "properties": { "id": "ci37757591", "mag": 1.24, "time": 1504844674340, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -116.899167, 34.321833, 9.91 ] } },
+      { "type": "Feature", "properties": { "id": "ak16821842", "mag": 2.0, "time": 1504843966513, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ 179.0405, 51.3724, 44.9 ] } },
+      { "type": "Feature", "properties": { "id": "ak16821841", "mag": 2.4, "time": 1504843627204, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -165.0538, 52.2197, 10.6 ] } },
+      { "type": "Feature", "properties": { "id": "ak16821840", "mag": 1.4, "time": 1504843458180, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -150.7807, 61.7731, 61.9 ] } },
+      { "type": "Feature", "properties": { "id": "ak16821839", "mag": 1.1, "time": 1504843388032, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -149.5616, 61.4081, 45.5 ] } },
+      { "type": "Feature", "properties": { "id": "ak16777570", "mag": 1.9, "time": 1504843130740, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -173.812, 51.8664, 11.6 ] } },
+      { "type": "Feature", "properties": { "id": "ak16821837", "mag": 1.7, "time": 1504843122073, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -154.717, 58.7435, 117.7 ] } },
+      { "type": "Feature", "properties": { "id": "ak16821836", "mag": 1.3, "time": 1504842507708, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -150.7323, 60.5323, 52.2 ] } },
+      { "type": "Feature", "properties": { "id": "ci37757551", "mag": 1.47, "time": 1504841647940, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -116.7945, 33.496333, 3.33 ] } },
+      { "type": "Feature", "properties": { "id": "us2000ahus", "mag": 4.7, "time": 1504841042960, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -169.0592, 52.1619, 10.0 ] } },
+      { "type": "Feature", "properties": { "id": "ak16777419", "mag": 1.0, "time": 1504839731548, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -153.81, 64.7157, 15.2 ] } },
+      { "type": "Feature", "properties": { "id": "ak16777418", "mag": 2.4, "time": 1504839437977, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -174.7414, 52.1837, 13.2 ] } },
+      { "type": "Feature", "properties": { "id": "ak16821832", "mag": 2.4, "time": 1504839217735, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -169.2407, 52.3318, 38.5 ] } },
+      { "type": "Feature", "properties": { "id": "hv61900746", "mag": 2.1, "time": 1504839173590, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -154.978833, 19.772, 43.163 ] } },
+      { "type": "Feature", "properties": { "id": "ismpkansas70234763", "mag": 1.85, "time": 1504838718270, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -97.887167, 37.203, 6.15 ] } },
+      { "type": "Feature", "properties": { "id": "ci37757519", "mag": 1.49, "time": 1504838267430, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -118.945167, 34.213667, 19.49 ] } },
+      { "type": "Feature", "properties": { "id": "us2000ahub", "mag": 4.3, "time": 1504837583700, "felt": 823, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -97.683, 36.6996, 6.073 ] } },
+      { "type": "Feature", "properties": { "id": "ak16777416", "mag": 1.3, "time": 1504836895690, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -150.6982, 63.5777, 11.7 ] } },
+      { "type": "Feature", "properties": { "id": "uw61304877", "mag": 1.15, "time": 1504836765080, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -122.568333, 48.699167, 3.29 ] } },
+      { "type": "Feature", "properties": { "id": "us2000ahu8", "mag": 3.7, "time": 1504836433340, "felt": 2, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -111.4569, 42.6238, 5.0 ] } },
+      { "type": "Feature", "properties": { "id": "nc72888096", "mag": 2.03, "time": 1504835142230, "felt": 4, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -122.053667, 37.835167, 7.59 ] } },
+      { "type": "Feature", "properties": { "id": "ak16821829", "mag": 1.5, "time": 1504834613166, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -163.7652, 67.5597, 6.1 ] } },
+      { "type": "Feature", "properties": { "id": "hv61900626", "mag": 2.91, "time": 1504833891990, "felt": null, "tsunami": 0 }, "geometry": { "type": "Point", "coordinates": [ -155.011833, 19.399333, 2.609 ] } }
+      ]
+      }
+    """.trimIndent()
     private const val EARTHQUAKE_SOURCE_ID = "earthquakes"
     private const val HEATMAP_LAYER_ID = "earthquakes-heat"
     private const val HEATMAP_LAYER_SOURCE = "earthquakes"

Thus would appreciate more details from you, preferably the whole code snippet that's not working - I believe you should be able to isolate it pretty easily.

PavlosTze commented 1 year ago

@kiryldz have you seen my second comment? There is an error log being produced by Mapbox library, which isn't there for any version before 10.11.0. only on this new version it's there.

cleemansen commented 1 year ago

I have the same issue with version 10.11.0. Get the same error in the console and the layer is not present on my map:

Mapbox W [maps-android\GeoJsonSource]: GeoJsonSource (id=my-source-id) was not able to set data with feature(), featureCollection() or geometry() as there is no Style object.

I'm adding a feature collection to my GeoJsonSource via .featureCollection(). In the past this was independent of the style.

kiryldz commented 1 year ago

I have the same issue with version 10.11.0. Get the same error in the console and the layer is not present on my map:

Mapbox W [maps-android\GeoJsonSource]: GeoJsonSource (id=my-source-id) was not able to set data with feature(), featureCollection() or geometry() as there is no Style object.

I'm adding a feature collection to my GeoJsonSource via .featureCollection(). In the past this was independent of the style.

Can you please share full activity code reproducing the issue?

cleemansen commented 1 year ago

I reproduced it by patching this example in a simple way.

W [maps-android\GeoJsonSource]: GeoJsonSource (id=marker-source) was not able to set data with feature(), featureCollection() or geometry() as there is no Style object.

2009.patch

kiryldz commented 1 year ago

@cleemansen thanks for confirming, we will look into it and fix most likely in v10.12.0.

kiryldz commented 1 year ago

@cleemansen @PavlosTze - workaround for now is pretty straight-forward - generate GeoJsonSource when style is available, e.g.:

mapView.getMapboxMap().loadStyleUri(Style.MAPBOX_STREETS) { style ->
  // here          
}
PavlosTze commented 1 year ago

That's what we do, but the issue is still there. @cleemansen Have you had any luck? We ended up using version 10.10.2 until this is fixed.

cleemansen commented 1 year ago

Same for me. Moving the GeoJsonSource.featureCollection() call to the suggested location results still in missing layer and the same console warning:

mapView.getMapboxMap().getStyle { style ->
  // here
}
kiryldz commented 1 year ago

@cleemansen @PavlosTze perhaps I did not make it clear. Looking into patch from https://github.com/mapbox/mapbox-maps-android/issues/2009#issuecomment-1441879311 (where indeed data is not applied and warning is logged) - if you modify setting featureCollection(as well as geometry / data/ feature) inside mapboxMap.getStyle:

mapboxMap.getStyle { style ->
      markerSource
        .featureCollection(
          FeatureCollection.fromFeatures(
            markerCoordinates.map {
              Feature.fromGeometry(it)
            }
          )
        )
      style.addSource(markerSource)
      style.addImage(imageId, BitmapFactory.decodeResource(resources, R.drawable.blue_marker_view))
      style.addLayer(layer)
    }

everything will work. Creating GeoJsonSource object and setting featureCollection when there's no Style loaded does not work indeed since v10.11.0. I'll try to fix it early next week and then this will be included in v10.12.0-rc.1 coming out next week as well.

cleemansen commented 1 year ago

Thanks for clarification @kiryldz . Actually I did understood your initial instructions for the workaround but it did not work for me.

I applied your snippet to my patch. It does not work for me.

The missing part is to add the construction of markerSource (and layer?) into the style block, too. With this I see the layer (sometimes, more later).

mapboxMap.getStyle { style ->
      val markerSource = GeoJsonSource
        .Builder(sourceId)
        .build()
      markerSource
        .featureCollection(
          FeatureCollection.fromFeatures(
            markerCoordinates.map {
              Feature.fromGeometry(it)
            }
          )
        )
      style.addSource(markerSource)
      style.addImage(imageId, BitmapFactory.decodeResource(resources, R.drawable.blue_marker_view))
      val layer = symbolLayer(layerId, sourceId) {
        iconImage(imageId)
        iconAllowOverlap(true)
        iconOffset(listOf(0.0, -9.0))
      }
      style.addLayer(layer)
    }

So IMHO it is not only a problem calling featureCollection at the right place but also instantiating the GeoJsonSource. Can you adress this in your upcoming fix, too?

Also I noticed that it is not 100% stable. Sometimes (each x start of the sample app) the layer is missing even with this setup. Sorry for this vague description.

kiryldz commented 1 year ago

@cleemansen sorry, my bad in the code snippet above, correct one is:

// does not matter where to declare as it's empty
val markerSource = GeoJsonSource
        .Builder(sourceId)
        .build()
mapboxMap.getStyle { style ->
      style.addSource(markerSource)
      // adding source connects style object with the source, if calling before addSource there's race condition as
      // data passed to core on worker thread
      markerSource
        .featureCollection(
          FeatureCollection.fromFeatures(
            markerCoordinates.map {
              Feature.fromGeometry(it)
            }
          )
        )
      style.addImage(imageId, BitmapFactory.decodeResource(resources, R.drawable.blue_marker_view))
      val layer = symbolLayer(layerId, sourceId) {
        iconImage(imageId)
        iconAllowOverlap(true)
        iconOffset(listOf(0.0, -9.0))
      }
      style.addLayer(layer)
    }
cleemansen commented 1 year ago

Thanks @kiryldz for the comments. I've got the feeling about a race condition, too.

As my real app is a bit more complex and I still have problems working around this issue I'm trying to understand the best setup.

I have run the following two snippets 10 times on the emulator and count how often the layer appears (apply changes and restart activity in AS).

    mapboxMap.getStyle { style ->
      markerSource
        .featureCollection(
          FeatureCollection.fromFeatures(
            markerCoordinates.map {
              Feature.fromGeometry(it)
            }
          )
        )
      style.addSource(markerSource)
      style.addImage(imageId, BitmapFactory.decodeResource(resources, R.drawable.blue_marker_view))
      style.addLayer(layer)
    }
    mapboxMap.getStyle { style ->
      style.addSource(markerSource)
      markerSource
        .featureCollection(
          FeatureCollection.fromFeatures(
            markerCoordinates.map {
              Feature.fromGeometry(it)
            }
          )
        )
      style.addImage(imageId, BitmapFactory.decodeResource(resources, R.drawable.blue_marker_view))
      style.addLayer(layer)
    }

So can you can confirm that is also important to

  1. add the source
  2. create the feature collection

as soon as the style is ready?

cleemansen commented 1 year ago

Oh sorry, after reading it again you already clearly explained my last question:

adding source connects style object with the source, if calling before addSource there's race condition as data passed to core on worker thread

Thanks for this workaround!

kiryldz commented 1 year ago

Should be fixed in v10.12.0-rc.1 version coming out later this week. If issue persists, please feel free to re-open.