How to filter symbols with .addSymbol mabox_gl flutter - mapbox

I have a bit of an issue.
I am adding symbols to my map using the .addSymbol function
Future<Symbol> addSymbol(SymbolOptions options, [Map<dynamic, dynamic>? data])
And when doing so the name of layer that's get added seems to be generated. The layer name can look like this:
4jmVTazvBD_0
And if I then do
mapController.setFilter("4jmVTazvBD_0",true) // or false
Then it filter it. But since the layerId changes every time the app starts and adds the symbols its not possible.
When I add my symbols I first get them from backend and then I loop through every item in the response from backend and add them like this:
mapController.addSymbol(
SymbolOptions(
draggable: true,
iconImage: item?.properties?.image,
geometry:
LatLng(item!.location!.latitude!, item.location!.longitude!),
iconSize: 1),
{
"type": item.properties?.type,
"annotation": item,
"id": item.id,
"lat": item.location?.latitude,
"lng": item.location?.longitude
},
);
Do some someone know how to filter them, just want to be able to toggle them visible or not.
My first approach*
I have tested to add a symbollayer and then add the object with points but nothing shows up.
I have tried the following with adding a symbolLayer:
I have a list with maps that I save that looks like this:
List<Map<String, dynamic>> features = [];
Map<String, dynamic> _symbols = {};
First I setup some thing in the onStyleLoadedCallbak like this:
void _onStyleLoadedCallback() async {
await mapController.addGeoJsonSource("points", _symbols);
await mapController.addSymbolLayer(
"points",
"symbols",
SymbolLayerProperties(
iconImage: "{type}",
iconSize: 2,
iconAllowOverlap: true,
),
);
}
addSymbolsToList(Annotations? item) {
if (features.any((element) => element["id"] == item?.id)) {
} else {
features.add(
{
"type": "Feature",
"id": item?.id,
"properties": {
"type": "pin_offline",
},
"geometry": {
"type": "Point",
"coordinates": [item?.location?.longitude, item?.location?.latitude]
}
},
);
}
}
But the code I tested when creating a symbolLayer don't add my symbols.

Related

Flutter mapbox_gl how to put polylines below symbols

I am trying to add polylines to a mapbox view in Flutter using the mapbox_gl library. The problem is that I also have markers (called symbols in the library) that need to be on top of the polylines, but it keeps drawing the polylines over the symbols.
This is the function I use to add the symbols:
_mbController.addSymbol(SymbolOptions(
geometry:
LatLng(vehicle['position']['lat'], vehicle['position']['lon']),
iconImage: 'vehicle'));
Then I use these functions to add the polylines:
Future<void> updatePolyLines(var geometry) async {
_fills = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": 0,
"properties": <String, dynamic>{},
"geometry": geometry,
},
],
};
await _mbController!.removeLayer("lines");
await _mbController!.removeSource("fills");
await _mbController!
.addSource("fills", GeojsonSourceProperties(data: _fills));
await _mbController!.addLineLayer(
"fills",
"lines",
const LineLayerProperties(
lineColor: '#007AFF',
lineCap: "round",
lineJoin: "round",
lineWidth: 4,
));
}
Future<void> showNavLine(LatLng destination) async {
LocationTuple l = await getLocation(context);
if (l.gotPosition) {
var response = await prepareRoute(l.latLng, destination);
if (response == null) {
return;
} else {
updatePolyLines(response['routes'][0]['geometry']);
}
}
}
The prepareRoute function uses the mapbox API to get a route, this part is working fine and the line is shown. However, as I said it is draw on top of the symbols.
I read that you can draw the polylines below a certain layer. So I tried adding a separate layer for the symbols and drawing the polylines below that, but I suspect the symbols are not put on that specific layer. I can't even set a specific layer in the addSymbol function of MapboxController.
Please advise. Btw all the examples I found online so far just draw the lines over the symbols. Drawing the lines first and then the symbols doesn't work.
I figured out how to put symbols in layers. Also, I was using [lat, lon] while it should have been [lon, lat]..
As long as you add the symbolLayer after the polyline layer the symbols will be drawn on top of the lines. Here is a working example (coordinates have been removed for privacy reasons but the working code is there):
var geometry = {
"coordinates": [
[lon, lat],
[lon, lat],
[lon, lat],
[lon, lat],
[lon, lat],
[lon, lat],
[lon, lat]
],
"type": "LineString"
};
_fills = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": 0,
"properties": <String, dynamic>{},
"geometry": geometry,
},
],
};
await _mbController!.removeLayer("lines");
await _mbController!.removeSource("fills");
await _mbController!
.addSource("fills", GeojsonSourceProperties(data: _fills));
await _mbController!.addLineLayer(
"fills",
"lines",
const LineLayerProperties(
lineColor: '#007AFF',
lineCap: "round",
lineJoin: "round",
lineWidth: 4,
),
);
await _mbController!.addSymbolLayer(
"scooter25_layer",
"vehicles",
const SymbolLayerProperties(
iconImage: "scooter25",
iconAllowOverlap: true,
),
);
const Map<String, dynamic> featureCollection = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [lon, lat],
}
}
]
};
await _mbController!.addGeoJsonSource("scooter25_layer", featureCollection);

Use Json data file on Flutter unit test

I would like to do the unit test for model by Flutter.
Then I want to use test json data.
But I got the error message.
type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>' in type cast
I want to know how to solve this issue.
Here is the test codes.
void main() {
group('Gift Test', () {
test('Gift model test', () {
final file = File('json/gift_test.json').readAsStringSync();
final gifts = Gift.fromJson(jsonDecode(file) as Map<String, dynamic>);
expect(gifts.id, 999);
});
});
}
Here is the model
#freezed
abstract class Gift with _$Gift {
#Implements(BaseModel)
const factory Gift({
int id,
String name,
int amount,
Image image,
}) = _Gift;
factory Gift.fromJson(Map<String, dynamic> json) => _$GiftFromJson(json);
}
And this is the test data.
[
{
"id": 999,
"name": "testest",
"amount": 30000,
"image": {
"id": 9999,
"image": {
"url": "https://text.jpg",
},
},
}
]
Your json file is a list, not an object, either you change your json file in
{
"id": 999,
"name": "testest",
"amount": 30000,
"image": {
"id": 9999,
"image": {
"url": "https://text.jpg",
},
},
}
Or you will need to update your code to take the first element of the list:
final file = File('json/gift_test.json').readAsStringSync();
final gifts = Gift.fromJson((jsonDecode(file) as List).first as Map<String, dynamic>);

Leaflet FeatureCollection: LatLong is undefined

I am looking to create a GeoJson instance from a database request with PHP.
I am using this package to create the instance:
http://jmikola.github.io/geojson/api/class-GeoJson.Feature.FeatureCollection.html
I have created the FeatureCollection but I am getting an error
"Error: Invalid LatLng object: (undefined, undefined)"
even though my coordinates are floats.
However, the coordinates are not coming through an array [,], but as a Javascript objects {,} that are inside an array and maybe this is the problem?
Secondly, I think I am having trouble setting the CRS. I can successfully create the GeoJSON in QGIS and it appears at the top of the FeatureCollection but with this package in PHP it appears towards the end.
My feature collection looks like this:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "MultiPoint",
"coordinates": [
{...},
{...}
]
},
"properties": [
{...},
{...}
],
"id": {
"type": "name",
"properties": {
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
}
}
}
]
}
I hope someone can help because I have been trawling through forums for hours!
Cheers
Yes, the coordinates and properties must be arrays, and not objects. Therefore, you can transform those before trying to create your geoJSON, like this:
//loop through all your features
featureCollection.features.forEach(function(feature) {
// first the coordinates in the geometry property
feature.geometry.coordinates.forEach(function(coord, index) {
feature.geometry.coordinates[index] = [coord.lat, coord.lng];
});
// then those in the properties
feature.properties.forEach(function(prop, index) {
feature.properties[index] = [prop.lat, prop.lng];
});
});

Access geojson property of a layer in a featuregroup in Mapbox

I have a GEOJSON which I added to the featuregroup in my Mapbox map like this:
var featureCollection = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"id": 1
},
"geometry": {
"type": "Point",
"coordinates": [0, 0]
}
},{
"type": "Feature",
"properties": {
"id": 2
},
"geometry": {
"type": "Point",
"coordinates": [30, 30]
}
},{
"type": "Feature",
"properties": {
"id": 3
},
"geometry": {
"type": "Point",
"coordinates": [-30, -30]
}
}]
};
var geojson = L.geoJson(featureCollection);
var featureGroup = L.featureGroup().addTo(map);
featureGroup.addLayer(geojson);
Now, I wish to access the id property of each layer while looping through the featuregroup, so that I can pass it as an argument to another function. In the case of a featurelayer, I can easily access it using something like this:
var featureLayer = L.mapbox.featureLayer(featureCollection).addTo(map);
featureLayer.eachLayer(function (layer) {
layer.on('click', function (e) {
console.log('Clicked feature ID: ' + e.target.feature.properties.id);
});
});
But I want to be able to access it while looping inside a featuregroup, and also I want to be able to do it without a 'click' or any such event. For example, ideally I would use something like this:
featureGroup.eachLayer(function (layer) {
var id = layer.feature.properties.id;
testfunction(id);
});
I haven't been able to figure out how to do that. I've tried searching online, but because I'm new to this, I probably haven't been using the right keywords in my search.
geojson is nested within featureGroup, so when your eachLayer function runs, it is not operating on the individual features within geojson, but instead on geojson itself. To extract each feature's id property, you will need to go one level deeper and iterate over the features within geojson.
Fortunately, the L.GeoJson class also supports the eachLayer method (because it is an extension of L.FeatureGroup, which is itself an extension of L.LayerGroup). To print the id of each feature, you could just use the eachLayer method directly on geojson:
geojson.eachLayer(function (layer) {
var id = layer.feature.properties.id;
testfunction(id);
});
Or, if you have a bunch of L.GeoJson objects nested within featureGroup, you can use:
featureGroup.eachLayer(function (layer) {
layer.eachLayer(function (layer) {
var id = layer.feature.properties.id;
testfunction(id);
});
});

can't implement L.mapbox.simplestyle with geoJson

I'm trying to implement simplestyle's options for marker size and color , but can't get them to render. In this simple test case, I'm trying to follow Mapbox's own example quite closely:
var myData = [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [4.509373,51.932994]
},
"properties": {
"marker-size": "large",
"marker-color": "#ffcc00"
}
}
];
var map = L.mapbox.map('map', 'examples.map-20v6611k')
.setView([51.932994,4.509373], 8);
L.geoJson(myData, { style: L.mapbox.simplestyle.style }).addTo(map);
fiddle
But the marker renders in default style. What am I missing?
OK, I've got it working using this extended function from this page of Mapbox's documentation:
L.geoJson(myData, {
pointToLayer: L.mapbox.marker.style,
style: function(feature) { return feature.properties; }
}).addTo(map);
The other Mapbox example didn't make it look like the pointToLayer argument was required, but whatever works:
fiddle
Another alternative would be to create a featureLayer based on your myData:
var featureLayer = L.mapbox.featureLayer(myData).addTo(map);
Your data will have to be an object, however, and not an array:
var myData = {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [4.509373,51.932994]
},
"properties": {
"marker-size": "large",
"marker-color": "#ffcc00"
}
};