fluttermapboxmapbox-glmapbox-android

Add source and layer from GeoJSON data in flutter mapbox gl


I'm building a simple Flutter application using the mapbox_gl: ^0.16.0 plugin. 1: https://i.sstatic.net/vh4Mp.png So basically i have a GeoJSON data, which i need to add in rendered map as source and apply a Fill Layer based on source (GeoJSON) property. Currently feature with fill layer is showing properly, but when try to get the feature or any feature details when user click on feature layer,it shows the "id" blank.

I've reviewed the Mapbox Maps documentation, but I couldn't find a clear example for achieving this specific functionality. Any assistance or code snippets would be highly appreciated. Thank you

Here is my code snippets,

/* view code */
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:mapbox_gl/mapbox_gl.dart';
import '../controllers/home_controller.dart';

class HomeView extends GetView<HomeController> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: SizedBox(
          height: Get.height,
          child: MapboxMap(
            onMapCreated: (mapcontroller) {
              controller.onMapCreated(mapcontroller);
            },
            zoomGesturesEnabled: true,
            accessToken:
                "<MAPBOX ACCESS TOKEN>",
            initialCameraPosition: const CameraPosition(
                target: LatLng(47.616100673, -107.882000467), zoom: 10),
          ),
        ),
      ),
    );
  }
}
/* view code */

/* controller code */
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:get/get.dart';
import 'package:mapbox_gl/mapbox_gl.dart';

class HomeController extends GetxController {
  String geojsonString = "";

  // Create a Map to store colors based on status
  Map<int, Color> statusColors = {
    1: const Color.fromARGB(128, 221, 143, 137), // 50% opacity
    2: const Color.fromARGB(128, 68, 235, 73), // 50% opacity
    3: const Color.fromARGB(128, 14, 125, 215), // 50% opacity
    4: Colors.transparent, // Custom color for status 4
  };

  @override
  void onInit() {
    super.onInit();
  }

  @override
  void onReady() {
    super.onReady();
  }

  @override
  void onClose() {}

  Future<void> loadGeoJSON() async {
    geojsonString = await rootBundle.loadString('assets/geo.geojson');
  }

  void onMapCreated(MapboxMapController mapboxMap) async {
    // Create a Map to store colors based on status
    Map<int, Color> statusColors = {
      1: const Color.fromARGB(128, 221, 143, 137), // 50% opacity
      2: const Color.fromARGB(128, 68, 235, 73), // 50% opacity
      3: const Color.fromARGB(128, 14, 125, 215), // 50% opacity
      4: Colors.transparent, // Custom color for status 4
    };

    // Add the GeoJSON source to the map
    await mapboxMap.addSource("fills", GeojsonSourceProperties(data: geojson));

    // Add the fill layer with the specified color and filter
    await mapboxMap.addFillLayer(
      "fills",
      "fills",
      const FillLayerProperties(
        fillColor: 'rgba(0, 0, 255, 0.4)', // Use a valid color string
        fillOpacity: 0.4,
      ),
      filter: [
        '==',
        ['get', 'status'],
        1 // Note: Removed quotes around 1 to compare with a number
      ],
    );

    mapboxMap.onFeatureTapped.add((id, point, coordinates) {
      // Check if the tapped feature is part of the "fills" layer
      if (id != null && id is String && id.startsWith('fills')) {
        // Extract the original feature ID
        String originalFeatureId = id.replaceAll('fills.', '');
        print(
            'Tapped feature with original ID - $originalFeatureId, Point - $point, Coordinates - $coordinates');
      }
    });
  }
  
}
/* controller code*/

/* sample GeoJSON */
{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              -107.900000467,
              47.598100673
            ],
            [
              -107.891000467,
              47.598100673
            ],
            [
              -107.891000467,
              47.589100673
            ],
            [
              -107.900000467,
              47.589100673
            ],
            [
              -107.900000467,
              47.598100673
            ]
          ]
        ]
      },
      "properties": {
        "dbId": "5",
        "status": 4,
        "uid": null,
        "isBlocked": 0,
        "id": "5",
        "left": -107.900000467,
        "top": 47.598100673,
        "right": -107.891000467,
        "bottom": 47.589100673
      }
    },...    
  ]
}
/* sample GeoJSON code */

Solution

  • This block of code works fine for me!

    void onMapCreated(MapboxMapController mapboxMap) async {
        // await getJson();
    
        // Create a Map to store colors based on status
        Map<int, String> statusColors = {
          1: convertColorToRgba(const Color.fromARGB(128, 221, 143, 137), 1),
          2: convertColorToRgba(const Color.fromARGB(128, 68, 235, 73), 1),
          3: convertColorToRgba(const Color.fromARGB(128, 14, 125, 215), 1),
          4: 'rgba(0, 0, 255, 0.0)', // Transparent color for status 4
        };
    
        // Add the GeoJSON source to the map
        await mapboxMap.addSource("fills", GeojsonSourceProperties(data: geojson));
    
        // Add the fill layer with the specified color and filter
        await mapboxMap.addFillLayer(
          "fills",
          "fills",
          FillLayerProperties(
            fillColor: [
              "step", ["get", "status"],
              statusColors[1], 1,
              statusColors[2], 2,
              statusColors[3], 3,
              statusColors[4], 4,
              'rgba(0, 0, 255, 0.0)' // Default color
            ],
            fillOpacity: 0.4,
            fillOutlineColor: 'rgba(0, 0, 0, 1)',
          ),
          filter: [
            "has",
            "status"
          ], // Add filter to only render features with a "status" property
          enableInteraction: true,
        );
        
        update();
      }