1679605560
Create a personal running home page
Running page runners
automatically backup gpx data for easy backup and uploading to other software.
Note: If you don't want to make the data public, you can choose strava's fuzzy processing, or private repositories.
Clone or fork the repo.
git clone https://github.com/yihong0618/running_page.git --depth=1
pip3 install -r requirements.txt
yarn install
yarn develop
Open your browser and visit http://localhost:8000/
#build
# NRC
docker build -t running_page:latest . --build-arg app=NRC --build-arg nike_refresh_token=""
# Garmin
docker build -t running_page:latest . --build-arg app=Garmin --build-arg email="" --build-arg password=""
# Garmin-CN
docker build -t running_page:latest . --build-arg app=Garmin-CN --build-arg email="" --build-arg password=""
# Strava
docker build -t running_page:latest . --build-arg app=Strava --build-arg client_id="" --build-arg client_secret="" --build-arg refresch_token=""
#Nike_to_Strava
docker build -t running_page:latest . --build-arg app=Nike_to_Strava --build-arg nike_refresh_token="" --build-arg client_id="" --build-arg client_secret="" --build-arg refresch_token=""
#run
docker run -itd -p 80:80 running_page:latest
#visit
Open your browser and visit localhost:80
src/utils/const.js
If you use English please change
IS_CHINESE = false
insrc/utils/const.js
Suggested changes to your own Mapbox token
const MAPBOX_TOKEN =
'pk.eyJ1IjoieWlob25nMDYxOCIsImEiOiJja2J3M28xbG4wYzl0MzJxZm0ya2Fua2p2In0.PNKfkeQwYuyGOTT_x9BJ4Q';
gatsby-config.js
in the repository directory, find the following content, and change it to what you want.siteMetadata: {
siteTitle: 'Running Page', #website title
siteUrl: 'https://yihong.run', #website url
logo: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQTtc69JxHNcmN1ETpMUX4dozAgAN6iPjWalQ&usqp=CAU', #logo img
description: 'Personal site and blog',
navLinks: [
{
name: 'Blog', #navigation name
url: 'https://yihong.run/running', #navigation url
},
{
name: 'About',
url: 'https://github.com/yihong0618/running_page/blob/master/README-CN.md',
},
],
},
src/utils/const.js
// styling: set to `false` if you want to disable dash-line route
const USE_DASH_LINE = true;
// styling: route line opacity: [0, 1]
const LINE_OPACITY = 0.4;
total
pageMake your GPX
data
Copy all your gpx files to GPX_OUT or new gpx files
python3(python) scripts/gpx_sync.py
Make your TCX
data
Copy all your tcx files to TCX_OUT or new tcx files
python3(python) scripts/tcx_sync.py
Get your Garmin
data
If you only want to sync `type running` add args --only-run If you only want `tcx` files add args --tcx
python3(python) scripts/garmin_sync.py ${your email} ${your password}
example:
python3(python) scripts/garmin_sync.py example@gmail.com example
only-run:
python3(python) scripts/garmin_sync.py example@gmail.com example --only-run
Get your Garmin-CN
data
If you only want to sync `type running` add args --only-run If you only want `tcx` files add args --tcx
python3(python) scripts/garmin_sync.py ${your email} ${your password} --is-cn
example:
python3(python) scripts/garmin_sync.py example@gmail.com example --is-cn
Get your Nike Run Club
data
Please note: When you choose to deploy running_page on your own server, due to Nike has blocked some IDC's IP band, maybe your server cannot sync Nike Run Club's data correctly and display
403 error
, then you have to change another way to host it.
Get Nike's refresh_token
refresh_token
python3(python) scripts/nike_sync.py ${nike refresh_token}
example:
python3(python) scripts/nike_sync.py eyJhbGciThiMTItNGIw******
Get your Strava
data
Sign in/Sign up Strava account
Open after successful Signin Strava Developers -> Create & Manage Your App
Create My API Application
: Enter the following information
Created successfully:
${your_id}
in the link with My API Application
Client IDhttps://www.strava.com/oauth/authorize?client_id=${your_id}&response_type=code&redirect_uri=http://localhost/exchange_token&approval_prompt=force&scope=read_all,profile:read_all,activity:read_all,profile:write,activity:write
code
value in the linkexample:
http://localhost/exchange_token?state=&code=1dab37edd9970971fb502c9efdd087f4f3471e6e&scope=read,activity:write,activity:read_all,profile:write,profile:read_all,read_all
code
value:
1dab37edd9970971fb502c9efdd087f4f3471e6
Client_id
、Client_secret
、Code
get refresch_token
: Execute in Terminal/iTerm
curl -X POST https://www.strava.com/oauth/token \
-F client_id=${Your Client ID} \
-F client_secret=${Your Client Secret} \
-F code=${Your Code} \
-F grant_type=authorization_code
example:
curl -X POST https://www.strava.com/oauth/token \
-F client_id=12345 \
-F client_secret=b21******d0bfb377998ed1ac3b0 \
-F code=d09******b58abface48003 \
-F grant_type=authorization_code
Strava
datapython3(python) scripts/strava_sync.py ${client_id} ${client_secret} ${refresch_token}
References:
upload all tcx files to strava
python3(python) scripts/tcx_to_strava_sync.py ${client_id} ${client_secret} ${strava_refresch_token}
example:
python3(python) scripts/tcx_to_strava_sync.py xxx xxx xxx
or
python3(python) scripts/tcx_to_strava_sync.py xxx xxx xxx --all
--all
upload all gpx files to strava
python3(python) scripts/gpx_to_strava_sync.py ${client_id} ${client_secret} ${strava_refresch_token}
example:
python3(python) scripts/gpx_to_strava_sync.py xxx xxx xxx
or
python3(python) scripts/tcx_to_strava_sync.py xxx xxx xxx --all
--all
Get your Nike Run Club
data and upload to strava
python3(python) scripts/nike_to_strava_sync.py ${nike_refresh_token} ${client_id} ${client_secret} ${strava_refresch_token}
example:
python3(python) scripts/nike_to_strava_sync.py eyJhbGciThiMTItNGIw****** xxx xxx xxx
Get your Garmin
data and upload to strava
python3(python) scripts/garmin_to_strava_sync.py ${client_id} ${client_secret} ${strava_refresch_token} ${garmin_email} ${garmin_password} --is-cn
e.g.
python3(python) scripts/garmin_to_strava_sync.py xxx xxx xxx xx xxx
Get your Strava
data and upload to Garmin
secrets.STRAVA_EMAIL
、secrets.STRAVA_PASSWORD
python3(python) scripts/strava_to_garmin_sync.py ${{ secrets.STRAVA_CLIENT_ID }} ${{ secrets.STRAVA_CLIENT_SECRET }} ${{ secrets.STRAVA_CLIENT_REFRESH_TOKEN }} ${{ secrets.GARMIN_EMAIL }} ${{ secrets.GARMIN_PASSWORD }} ${{ secrets.STRAVA_EMAIL }} ${{ secrets.STRAVA_PASSWORD }}
if your garmin account region is China, you need to execute the command:
python3(python) scripts/strava_to_garmin_sync.py ${{ secrets.STRAVA_CLIENT_ID }} ${{ secrets.STRAVA_CLIENT_SECRET }} ${{ secrets.STRAVA_CLIENT_REFRESH_TOKEN }} ${{ secrets.GARMIN_CN_EMAIL }} ${{ secrets.GARMIN_CN_PASSWORD }} ${{ secrets.STRAVA_EMAIL }} ${{ secrets.STRAVA_PASSWORD }} --is-cn
ps: when initializing for the first time, if you have a large amount of strava data, some data may fail to upload, just retry several times.
Running data display
python scripts/gen_svg.py --from-db --title "${{ env.TITLE }}" --type github --athlete "${{ env.ATHLETE }}" --special-distance 10 --special-distance2 20 --special-color yellow --special-color2 red --output assets/github.svg --use-localtime --min-distance 0.5
python scripts/gen_svg.py --from-db --title "${{ env.TITLE_GRID }}" --type grid --athlete "${{ env.ATHLETE }}" --output assets/grid.svg --min-distance 10.0 --special-color yellow --special-color2 red --special-distance 20 --special-distance2 40 --use-localtime
Generate year circular svg show
python3(python) scripts/gen_svg.py --from-db --type circular --use-localtime
For more display effects, see: https://github.com/flopp/GpxTrackPoster
Use Vercel
to deploy
Use Cloudflare
to deploy
Click Create a project
in Pages
to connect to your Repo.
After clicking Begin setup
, modify Project's Build settings
.
Select Framework preset
to Gatsby
Scroll down, click Environment variables
, then variable below:
Variable name =
PYTHON_VERSION
, Value =3.7
Click Save and Deploy
Deploy to GitHub Pages
If you are using a custom domain for GitHub Pages, open .github/workflows/gh-pages.yml, change fqdn
value to the domain name of your site.
(Skip this step if you're NOT using a custom domain) Modify gatsby-config.js
, change pathPrefix
value to the root path. If the repository name is running_page
, the value will be /running_page
.
Go to repository's Actions -> Workflows -> All Workflows
, choose Publish GitHub Pages
from the left panel, click Run workflow
. Make sure the workflow runs without errors, and gh-pages
branch is created.
Go to repository's Settings -> GitHub Pages -> Source
, choose Branch: gh-pages
, click Save
.
Modifying information in GitHub Actions
Actions source code The following steps need to be taken
Settings -> Code and automation -> Actions ->General
, Scroll to the bottom, find Workflow permissions
, choose the first option Read and write permissions
, click Save
.TODO
Contribution
Before submitting PR:
black
(black .
)Special thanks
Recommended Forks
Support
Just enjoy it~
FAQ
https://www.strava.com/settings/api https://developers.strava.com/docs/#rate-limiting
Strava API Rate Limit Exceeded. Retry after 100 seconds
Strava API Rate Limit Timeout. Retry in 799.491622 seconds
Author: yihong0618
Source Code: https://github.com/yihong0618/running_page
License: MIT license
1670523120
When it comes to modern web applications, interactions often need to be done in real-time. This means that instead of periodically checking in for changes, watching or listening for changes often makes more sense.
Take the example of tracking something on a map. When it comes to package shipments, device tracking, or anything else where you need to know the real-time location, watching for those changes in location is great. Imagine needing to know where your fleet is so that you can dispatch them to a nearby incident?
When it comes to MongoDB, watching for changes can be done through change streams. These change streams can be used in any of the drivers, including front-end applications with MongoDB Realm.
In this tutorial, we’re going to leverage MongoDB Realm change streams. When the location data in our NoSQL documents change, we’re going to update the information on an interactive map powered by Mapbox.
Take the following animated image for example:
Rather than building an Internet of Things (IoT) device to track and submit GPS data, we’re going to simulate the experience by directly changing our documents in MongoDB. When the update operations are complete, the front-end application with the interactive map is watching for those changes and responding appropriately.
To be successful with this example, you’ll need to have a few things ready to go prior:
For this example, the data will exist in MongoDB Atlas. Since we’re planning on interacting with our data using a front-end application, we’ll be using MongoDB Realm. A Realm application should be created within the MongoDB Cloud and connected to the MongoDB Atlas cluster prior to exploring this tutorial.
Get started with MongoDB Atlas and Realm for FREE in the MongoDB Cloud.
Mapbox will be used as our interactive map. Since Mapbox is a service, you’ll need to have created an account and have access to your access token.
In the animated image, I’m using the MongoDB Visual Studio Code plugin for interacting with the documents in my collection. You can do the same or use another tool such as Compass, the CLI, or the data explorer within Atlas to get the job done.
Because we’re only planning on moving a marker around on a map, the data model that we use doesn’t need to be extravagant. For this example, the following is more than acceptable:
{
"_id": "5ec44f70fa59d66ba0dd93ae",
"coordinates": [
-121.4252,
37.7397
],
"username": "nraboy"
}
In the above example, the coordinates array has the first item representing the longitude and the second item representing the latitude. We’re including a username to show that we are going to watch for changes based on a particular document field. In a polished application, all users probably wouldn’t be watching for changes for all documents. Instead they’d probably be watching for changes of documents that belong to them.
While we could put authorization rules in place for users to access certain documents, it is out of the scope of this example. Instead, we’re going to mock it.
Now we’re going to build our client-facing application which consists of Mapbox, some basic HTML and JavaScript, and MongoDB Realm.
Let’s start by adding the following boilerplate code:
<!DOCTYPE html>
<head>
<script src="https://api.mapbox.com/mapbox-gl-js/v1.10.0/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.10.0/mapbox-gl.css" rel="stylesheet" />
<script src="https://s3.amazonaws.com/stitch-sdks/js/bundles/4.6.0/stitch.js"></script>
</head>
<html>
<body style="margin: 0">
<div id="map" style="width: 100vw; height: 100vh"></div>
<script>
// Logic here ...
</script>
</body>
</html>
The above code sets us up by including the Mapbox and MongoDB Realm SDKs. When it comes to querying MongoDB and interacting with the map, we’re going to be doing that from within the <script>
tags.
Within the <script>
tags, let’s get started by adding the following:
const client = stitch.Stitch.initializeDefaultAppClient('REALM_APP_ID_HERE');
const db = client.getServiceClient(stitch.RemoteMongoClient.factory, "mongodb-atlas").db("location_services");
mapboxgl.accessToken = "MAPBOX_ACCESS_TOKEN_HERE";
var currentLocationMarker;
The above lines of code are useful for the initialization of our services. You’ll want to swap the app id with your actual Realm app id and the Mapbox access token with your actual Mapbox access token. Both of these can be found within each of the services dashboards.
For this example, I’m going to assume you’re using a location_services database within MongoDB Atlas and a tracking collection within that database.
The currentLocationMarker
variable will represent our changing marker that moves around on the map as new data comes in from the MongoDB change stream.
Within the same <script>
tags, we can initialize the map for displaying:
let map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/mapbox/streets-v11",
center: [-121.4252, 37.7397],
zoom: 9
});
Since the map tiles that compose the map come from a service, we need to wait until the map is ready before we start interacting with it. We can make use of the Mapbox load
event to let us know the map is ready:
map.on("load", async () => {
await client.auth.loginWithCredential(new stitch.AnonymousCredential());
let currentLocation = (await db.collection("tracking").findOne({ "username": "nraboy" })).coordinates;
currentLocationMarker = new mapboxgl.Marker().setLngLat(currentLocation).addTo(map);
const stream = await db.collection("tracking").watch({
"fullDocument.username": "nraboy"
});
stream.onNext(event => {
currentLocationMarker.setLngLat(event.fullDocument.coordinates);
});
});
Inside the load
event, we are doing anonymous authentication with MongoDB Realm. Remember, we could very easily use a stronger authentication method and have authorization rules, but for this example it’s out of the scope.
Once we’re authenticated to Realm, we execute a findOne
operation based on the mock username
field on our document. The only data we care about is the coordinates, but we want to make sure it comes from the correct username
field.
With the latitude and longitude coordinate information in hand, we can update the marker position.
Up until now, we are just setting the marker to the last known position. This is because when we start watching with a change stream, we won’t receive an initial document. This is why we are doing the findOne
first.
This brings us to the change stream:
const stream = await db.collection("tracking").watch({
"fullDocument.username": "nraboy"
});
stream.onNext(event => {
currentLocationMarker.setLngLat(event.fullDocument.coordinates);
});
For this particular change stream, we are only watching for documents where the username
field matches. In a more polished and realistic example, you could use this to watch for only your own documents. We’re mocking this by hard-coding the value.
When documents with the username
field experience some kind of change within Atlas, the marker location will update. This is done without us having to constantly query for updates based on a timer.
You just saw how to use change streams in a client-facing application using the MongoDB Realm SDK. To make our example more attractive, we went with a location tracking scenario, whereas when latitude and longitude locations change in the database, the positions are updated in real-time on a map powered by Mapbox.
If you’d like to give MongoDB Atlas and MongoDB Realm a try, there’s a forever FREE tier available through the MongoDB Cloud.
When it comes to location data and MongoDB, there’s so much more you can do. If you’d like to learn how to create and manage geofences, check out my previous tutorial titled, Location Geofencing with MongoDB, Realm, and Mapbox.
This content first appeared on MongoDB.
Original article source at: https://developer.mongodb.com/
1670495340
For a lot of organizations, when it comes to location, geofencing is often a very desirable or required feature. In case you’re unfamiliar, a geofence can be thought of as a virtual perimeter for a geographic area. Often, you’ll want to know when something enters or exits that geofence so that you can apply your own business logic. Such logic might include sending a notification or updating something in your database.
MongoDB supports GeoJSON data and offers quite a few operators that make working the location data easy.
When it comes to geofencing, why would you want to use a database like MongoDB rather than defining boundaries directly within your client-facing application? Sure, it might be easy to define and manage one or two boundaries, but when you’re working at scale, checking to see if something has exited or entered one of many boundaries could be a hassle.
In this tutorial, we’re going to explore the $near and $geoIntersects operators within MongoDB to define geofences and see if we’re within the fences. For the visual aspect of things, we’re going to make use of Mapbox for showing our geofences and our location.
To get an idea of what we’re going to build, take a look at the following animated image:
We’re going to implement functionality where a map is displayed and polygon shapes are rendered based on data from within MongoDB. When we move the marker around on the map to simulate actual changes in location, we’re going to determine whether or not we’ve entered or exited a geofence.
There are a few moving pieces for this particular tutorial, so it is important that the prerequisites are met prior to starting:
Mapbox is a service, not affiliated with MongoDB. To render a map along with shapes and markers, an account is necessary. For this example, everything can be accomplished within the Mapbox free tier.
Because we’ll be using MongoDB Stitch in connection with Mapbox, we’ll need to be using MongoDB Atlas.
MongoDB Atlas can be used to deploy an M0 sized cluster of MongoDB for FREE.
The MongoDB Atlas cluster should have a location_services database with a geofences collection.
To use the geospatial functionality that MongoDB offers, the data stored within MongoDB must be valid GeoJSON data. At the end of the day, GeoJSON is still JSON, which plays very nicely with MongoDB, but there is a specific schema that must be followed. To learn more about GeoJSON, visit the specification documentation.
For our example, we’re going to be working with Polygon and Point data. Take the following document model:
{
"_id": ObjectId(),
"name": string,
"region": {
"type": string,
"coordinates": [
[
[double]
]
]
}
}
In the above example, the region
represents our GeoJSON data and everything above it such as name
represents any additional data that we want to store for the particular document. A realistic example to the above model might look something like this:
{
"_id": ObjectId("5ebdc11ab96302736c790694"),
"name": "tracy",
"region": {
"type": "Polygon",
"coordinates": [
[
[-121.56115581054638, 37.73644193427164],
[-121.33868266601519, 37.59729761382843],
[-121.31671000976553, 37.777700170855454],
[-121.56115581054638, 37.73644193427164]
]
]
}
}
We’re naming any of our possible fenced regions. This could be useful to a lot of organizations. For example, maybe you’re a business with several franchise locations. You could geofence the location and name it something like the address, store number, etc.
To get the performance we need from our geospatial data and to be able to use certain operators, we’re going to need to create an index on our collection. The index looks something like the following:
db.geofences.createIndex({ region: "2dsphere" })
The index can be created through Atlas, Compass, and with the CLI. The goal here is to make sure the region
field is a 2dsphere
index.
Rather than creating a backend application to interact with the database, we’re going to make use of MongoDB Stitch. Essentially, the client-facing application will use the Stitch SDK to authenticate before interacting with the data.
Within the MongoDB Cloud, choose to create a new Stitch application if you don’t already have one that you wish to use. Make sure that the application is using the cluster that has your geofencing data.
Within the Stitch dashboard, choose the Rules tab and create a new set of permissions for the geofences collection. For this particular example, the Users can only read all data permission template is fine.
Next, we’ll want to choose an authentication mechanism. In the Users tab, choose Providers, and enable the anonymous authentication provider. In a more realistic production scenario, you’ll likely want to create geofences that have stricter users and rules design.
Before moving onto actually creating an application, make note of your App ID within Stitch, as it will be necessary for connecting.
With all the configuration out of the way, we can move into the fun part of creating an attractive client-facing application that queries the geospatial data in MongoDB and renders it on a map.
On your computer, create an index.html file with the following boilerplate code:
<!DOCTYPE html>
<head>
<script src="https://api.mapbox.com/mapbox-gl-js/v1.10.0/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.10.0/mapbox-gl.css" rel="stylesheet" />
<script src="https://s3.amazonaws.com/stitch-sdks/js/bundles/4.6.0/stitch.js"></script>
</head>
<html>
<body style="margin: 0">
<div id="map" style="width: 100vw; height: 100vh"></div>
<script>
// Logic in here ...
</script>
</body>
</html>
In the above HTML, we’re importing the Mapbox and MongoDB Stitch SDKs, and we are defining an HTML container to hold our interactive map. Interacting with MongoDB and the map will be done in the <script>
tag that follows.
Within the <script>
tag, the first things we want to accomplish are around connecting to MongoDB Stitch and configuring map:
const client = stitch.Stitch.initializeDefaultAppClient("MONGODB_STITCH_APP_ID_HERE");
const db = client.getServiceClient(stitch.RemoteMongoClient.factory, "mongodb-atlas").db("location_services");
let currentLocationMarker;
client.auth.loginWithCredential(new stitch.AnonymousCredential());
mapboxgl.accessToken = "MAPBOX_ACCESS_TOKEN_HERE";
let map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/mapbox/streets-v11",
center: [-121.4252, 37.7397],
zoom: 9
});
The map should be centered somewhere around Tracy, CA, and MongoDB Stitch was configured to use the location_services database. Make sure to swap the tokens with your actual Mapbox and Stitch tokens.
The next step is to populate the map with markers and polygons when it loads:
map.on("load", async () => {
currentLocationMarker = new mapboxgl.Marker().setLngLat([-121.29473735351542, 37.94575186984845]).addTo(map);
map.addSource("UNIQUE_ID", {
"type": "geojson",
"data": {
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-121.56115581054638, 37.73644193427164],
[-121.33868266601519, 37.59729761382843],
[-121.31671000976553, 37.777700170855454],
[-121.56115581054638, 37.73644193427164]
]
]
}
}
});
map.addLayer({
"id": "UNIQUE_ID",
"type": "fill",
"source": "UNIQUE_ID",
"layout": {},
"paint": {
"fill-color": "#088",
"fill-opacity": 0.8
}
});
});
In the above load
event, we are creating a marker somewhere outside the center of the map and one single polygon shape. The thing is, we don’t want to hard-code our polygon shapes that represent geofence regions. Instead, add the GeoJSON data to MongoDB along with other possible fences.
We can change our load
event to the following:
map.on("load", async () => {
let fences = await db.collection("geofences").find({}).asArray();
currentLocationMarker = new mapboxgl.Marker().setLngLat([-121.29473735351542, 37.94575186984845]).addTo(map);
fences.forEach(fence => {
map.addSource(fence.name, {
"type": "geojson",
"data": {
"type": "Feature",
"geometry": fence.region
}
});
map.addLayer({
"id": fence.name,
"type": "fill",
"source": fence.name,
"layout": {},
"paint": {
"fill-color": "#088",
"fill-opacity": 0.8
}
});
});
});
In the above code, we query our collection for all documents and add them each as a layer on the map. We can do better though. In the above example, the geofences amount could be quite large and it doesn’t necessarily make sense to show all the fences that aren’t even remotely close to the current location. This would slow down the application for the client.
Instead, we can change the query to the following:
let fences = await db.collection("geofences").find({
region: {
$near: {
$geometry: {
type: "Point",
coordinates: [-121.4252, 37.7397]
},
$maxDistance: 50000
}
}
}).asArray();
In the above code, we’re saying that we only want geofence results that are within 50,000 meters of our center point coordinate set. While our example doesn’t have many documents, this could be very beneficial in terms of performance.
Depending on the geofence data in MongoDB, you likely have some polygons drawn on the map as well as your marker. The next step is to move the marker around to simulate a change in location. We can do this with the click
event for Mapbox:
map.on("click", async (e) => {
currentLocationMarker.setLngLat([e.lngLat.lng, e.lngLat.lat]);
let result = await db.collection("geofences").find({
region: {
$geoIntersects: {
$geometry: {
type: "Point",
coordinates: [e.lngLat.lng, e.lngLat.lat]
}
}
}
}, { projection: { name: 1 }}).asArray();
if(result.length > 0) {
alert(`Within the ${result[0].name} fence!`);
}
});
In the above code we make use of the $geoIntersects
operator in our query. What this is doing is returning all documents where our point sits within the polygon shape. The marker is updated to wherever the map was clicked.
If there is an intersection, we just print out the first result to an alert. We do this because there could be overlapping geofences and for the scope of this example, we don’t need to worry about that.
You just saw how to leverage MongoDB and its ability to do geospatial queries to create geofences for a maps and location services type application. In the application we built, we stored GeoJSON data within MongoDB and queried for it using the $near
and $geoIntersects
operators. This allowed us to figure out what data we wanted based on a point location. We made use of Mapbox to give us a visual element as to whether or not our queries would return data.
Original article source at: https://developer.mongodb.com/
1670492353
When it comes to location data, MongoDB’s ability to work with GeoJSON through geospatial queries is often under-appreciated. Being able to query for intersecting or nearby coordinates while maintaining performance is functionality a lot of organizations are looking for.
Take the example of maintaining a list of business locations or even a fleet of vehicles. Knowing where these locations are, relative to a particular position isn’t an easy task when doing it manually.
In this tutorial we’re going to explore the $near
operator within a MongoDB Realm application to find stored points of interest within a particular proximity to a position. These points of interest will be rendered on a map using the Mapbox service.
To get a better idea of what we’re going to accomplish, take the following animated image for example:
We’re going to pre-load our MongoDB database with a few points of interest that are formatted using the GeoJSON specification. When clicking around on the map, we’re going to use the $near
operator to find new points of interest that are within range of the marker.
There are numerous components that must be accounted for to be successful with this tutorial:
The assumption is that MongoDB Atlas has been properly configured and that MongoDB Realm is using the MongoDB Atlas cluster.
MongoDB Atlas can be used for FREE with a M0 sized cluster. Deploy MongoDB in minutes within the MongoDB Cloud.
In addition to Realm being pointed at the Atlas cluster, anonymous authentication for the Realm application should be enabled and an access rule should be defined for the collection. All users should be able to read all documents for this tutorial.
In this example, Mapbox is a third-party service for showing interactive map tiles. An account is necessary and an access token to be used for development should be obtained. You can learn how in the Mapbox documentation.
Before diving into geospatial queries and creating an interactive client-facing application, a moment should be taken to understand the data and indexes that must be created within MongoDB.
Take the following example document:
{
"_id": "5ec6fec2318d26b626d53c61",
"name": "WorkVine209",
"location": {
"type": "Point",
"coordinates": [
-121.4123,
37.7621
]
}
}
Let’s assume that documents that follow the above data model exist in a location_services database and a points_of_interest collection.
To be successful with our queries, we only need to store the location type and the coordinates. This location
field makes up a GeoJSON feature, which follows a specific format. The name
field, while useful isn’t an absolute requirement. Some other optional fields might include an address
field, hours_of_operation
, or similar.
Before being able to execute the geospatial queries that we want, we need to create a special index.
The following index should be created:
db.points_of_interest.createIndex({ location: "2dsphere" });
The above index can be created numerous ways, for example, you can create it using the MongoDB shell, Atlas, Compass, and a few other ways. Just note that the location
field is being classified as a 2dsphere
for the index.
With the index created, we can execute a query like the following:
db.points_of_interest.find({
"location": {
"$near": {
"$geometry": {
"type": "Point",
"coordinates": [-121.4252, 37.7397]
},
"$maxDistance": 2500
}
}
});
Notice in the above example, we’re looking for documents that have a location
field within 2,500 meters of the point provided in the filter.
With an idea of how the data looks and how the data can be accessed, let’s work towards creating a functional application.
Like previously mentioned, you should already have a Mapbox account and MongoDB Realm should already be configured.
On your computer, create an index.html file with the following boilerplate code:
<!DOCTYPE html>
<head>
<script src="https://api.mapbox.com/mapbox-gl-js/v1.10.0/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v1.10.0/mapbox-gl.css" rel="stylesheet" />
<script src="https://s3.amazonaws.com/stitch-sdks/js/bundles/4.6.0/stitch.js"></script>
</head>
<html>
<body style="margin: 0">
<div id="map" style="width: 100vw; height: 100vh"></div>
<script>
// Logic here ...
</script>
</body>
</html>
In the above code, we’re including both the Mapbox library as well as the MongoDB Realm SDK. We’re creating a map
placeholder component which will show our map, and it is lightly styled with CSS.
You can run this file locally, serve it, or host it on MongoDB Realm.
Within the <script>
tags, we can initialize a few things with the following lines of code:
const client = stitch.Stitch.initializeDefaultAppClient("MONGODB_REALM_APP_ID_HERE);
const db = client.getServiceClient(stitch.RemoteMongoClient.factory, "mongodb-atlas").db("location_services");
mapboxgl.accessToken = "MAPBOX_ACCESS_TOKEN_HERE";
var currentLocationMarker = new mapboxgl.Marker({ color: "red" }).setLngLat([-121.4252, 37.7397]);
var placeMarkers = [];
let map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/mapbox/streets-v11",
center: [-121.4252, 37.7397],
zoom: 13
});
map.doubleClickZoom.disable();
In the above code we’re telling both MongoDB Realm and Mapbox which tokens to use. We’re also defining some basic configuration information for our map and which database to use for Realm.
The currentLocationMarker
will represent a mock of our location and the placesMarkers
will represent any nearby points of interest.
It should be noted, that we haven’t yet connected to MongoDB in our code. We want to wait until the map is ready, so we have to make use of event listeners.
Take the following code for example:
map.on("load", async () => {
await client.auth.loginWithCredential(new stitch.AnonymousCredential());
currentLocationMarker.addTo(map);
// Find points of interest near marker
});
When the map is considered ready, we do anonymous authentication to MongoDB Realm and we add our current location to the map as a marker. When the map is loaded, we’re also going to want to find any points of interest near our marker. This is where our geospatial query comes into play.
Outside of the event listener, create the following function:
const getPointsOfInterest = async (position) => {
let places = await db.collection("points_of_interest").find({
"location": {
"$near": {
"$geometry": {
"type": "Point",
"coordinates": position
},
"$maxDistance": 2500
}
}
}).toArray();
return places.map(place => (new mapboxgl.Marker().setLngLat(place.location.coordinates)).setPopup(new mapboxgl.Popup({ offset: 25 }).setText(place.name)));
};
Most of the function should look familiar. We’re accepting a position array and using it within the $near
operation of the query. The results of the query are loaded into an array and the results are then transformed into map markers that include a popup. The popup will show the name information for the point of interest.
With the above function in place, we can go back into the load
event listener and change it to the following:
map.on("load", async () => {
await client.auth.loginWithCredential(new stitch.AnonymousCredential());
currentLocationMarker.addTo(map);
placeMarkers = await getPointsOfInterest([-121.4252, 37.7397]);
placeMarkers.forEach(marker => marker.addTo(map));
});
Each marker returned from the getPointsOfInterest
function will be added to the map.
This is great, but we can take it a step further by interacting with the map and continuously doing queries based on our new location. Let’s add another event listener:
map.on("dblclick", async (e) => {
currentLocationMarker.setLngLat([e.lngLat.lng, e.lngLat.lat]);
placeMarkers.forEach(marker => marker.remove());
placeMarkers = await getPointsOfInterest([e.lngLat.lng, e.lngLat.lat]);
placeMarkers.forEach(marker => marker.addTo(map));
});
When the map is double clicked, the marker location is updated, all previous markers are removed, and new points of interest are queried for.
Want to search for specific points of interest that might be nearby? You can take your getPointsOfInterest
function a bit further by having a find
operation like the following:
let places = await db.collection("points_of_interest").find({
"location": {
"$near": {
"$geometry": {
"type": "Point",
"coordinates": position
},
"$maxDistance": 2500
}
},
"name": "Target"
}).toArray();
Notice that in the above code we’re saying that the location
has to be near our point and the name
has to be Target. The odds of having multiple Target stores within our range is slim, but imagine if Starbucks was used, or if your example included different kinds of data.
You just saw how to use the $near
operator to potentially find points of interest with geospatial queries in MongoDB. To make this example more exciting, we rendered our results on a map using Mapbox, and linked the client-facing Mapbox application to our database with MongoDB Realm.
If you’d like to see more Mapbox with MongoDB examples, check out my tutorial titled, Location Geofencing with MongoDB, Realm, and Mapbox.
Original article source at: https://www.mongodb.com/
1666344300
mapboxer
makes Mapbox GL JS, an open source JavaScript library that uses WebGL to render interactive maps, available within R via the htmlwidgets package.
mapboxer
provides bindings to v1.x.x of Mapbox GL JS, which is under the 3-Clause BSD license. See also breaking changes in Mapbox GL JS v2.0.0.
Install the release version from CRAN with:
install.packages("mapboxer")
Install the development version from GitHub with:
# install.packages("remotes")
remotes::install_github("crazycapivara/mapboxer")
library(mapboxer)
map <- motor_vehicle_collisions_nyc %>%
dplyr::mutate(
color = ifelse(injured == 0, "yellow", "red")
) %>%
as_mapbox_source() %>%
mapboxer(
center = c(-73.9165, 40.7114),
zoom = 10
) %>%
add_navigation_control() %>%
add_circle_layer(
circle_color = c("get", "color"),
circle_blur = 1,
circle_stroke_color = "red",
circle_stroke_width = 1,
popup = "<p>{{date}} {{time}}</p><p>Number of persons injured: {{injured}}</p>"
)
if (interactive()) map
By default mapboxer uses Carto vector styles as basemaps. It is also possible to use raster tiles or a background color.
If you want to use styles from Mapbox it is recommended that you store your API token in an environment vatiable called MAPBOX_API_TOKEN
.
All JavaScript code of mapboxer is located in javascript/src
.
Install dependencies and build the library with:
npm install
npm run build
The module is written to inst/htmlwidgets
.
Spin up the dev server with:
npm run start
Author: crazycapivara
Source Code: https://github.com/crazycapivara/mapboxer
License: Unknown, MIT licenses found
1665890236
Flutter Mapbox GL
Please note that this project is community driven and is not an official Mapbox product.
We welcome feedback and contributions.
This Flutter plugin allows to show embedded interactive and customizable vector maps inside a Flutter widget. For the Android and iOS integration, we use mapbox-gl-native. For web, we rely on mapbox-gl-js. This project only supports a subset of the API exposed by these libraries.
This package is available on pub.dev.
Get it by running the following command:
flutter pub add mapbox_gl
A secret access token with the Downloads: Read
scope is required for the underlying Mapbox SDKs to be downloaded. Information on setting it up is available in the Mapbox documentation: Android, iOS.
If the properly configured token is not present, the build process fails with one the following errors (for Android/iOS respectively):
* What went wrong:
A problem occurred evaluating project ':mapbox_gl'.
> SDK Registry token is null. See README.md for more information.
[!] Error installing Mapbox-iOS-SDK
curl: (22) The requested URL returned error: 401 Unauthorized
Include the JavaScript and CSS files in the <head>
of your index.html
file:
<script src='https://api.mapbox.com/mapbox-gl-js/v2.8.2/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v2.8.2/mapbox-gl.css' rel='stylesheet' />
<style>
.mapboxgl-map {
position: relative;
width: 100%;
height: 100%;
}
</style>
Note: Look for latest version in Mapbox GL JS documentation.
A public access token must be provided to a MapboxMap widget for retrieving styles and resources. While you can hardcode it directly into source files, it's good practise to retrieve access tokens from some external source (e.g. a config file or an environment variable). The example app uses the following technique:
The access token is passed via the command line arguments when either building
flutter build <platform> --dart-define ACCESS_TOKEN=YOUR_TOKEN_HERE
or running the application
flutter run --dart-define ACCESS_TOKEN=YOUR_TOKEN_HERE
Then it's retrieved in Dart:
MapboxMap(
...
accessToken: const String.fromEnvironment("ACCESS_TOKEN"),
...
)
Feature | Android | iOS | Web |
---|---|---|---|
Style | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Camera | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Gesture | :white_check_mark: | :white_check_mark: | :white_check_mark: |
User Location | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Style DSL | :x: | :x: | :x: |
Raster Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Symbol Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Circle Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Line Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Fill Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Fill Extrusion Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Hillshade Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Heatmap Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Vector Source | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Raster Source | :white_check_mark: | :white_check_mark: | :white_check_mark: |
GeoJson Source | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Image Source | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Expressions | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Symbol Annotation | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Circle Annotation | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Line Annotation | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Fill Annotation | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Map styles can be supplied by setting the styleString
in the MapOptions
. The following formats are supported:
MapboxStyles
or a custom map style served remotely using a URL that start with 'http(s)://' or 'mapbox://'assets
and add a reference in pubspec.yml
. Set the style string to the relative path for this asset in order to load it into the map.Support for offline maps is available by side loading the required map tiles and including them in your assets
folder.
Create your tiles package by following the guide available here.
Place the tiles.db file generated in step one in your assets directory and add a reference to it in your pubspec.yml
file.
assets:
- assets/cache.db
installOfflineMapTiles
when your application starts to copy your tiles into the location where Mapbox can access them. NOTE: This method should be called before the Map widget is loaded to prevent collisions when copying the files into place. try {
await installOfflineMapTiles(join("assets", "cache.db"));
} catch (err) {
print(err);
}
An offline region is a defined region of a map that is available for use in conditions with limited or no network connection. Tiles for selected region, style and precision are downloaded from Mapbox using proper SDK methods and stored in application's cache.
Beware of selecting big regions, as size might be significant. Here is an online estimator https://docs.mapbox.com/playground/offline-estimator/.
Call downloadOfflineRegionStream
with predefined OfflineRegion
and optionally track progress in the callback function.
final Function(DownloadRegionStatus event) onEvent = (DownloadRegionStatus status) {
if (status.runtimeType == Success) {
// ...
} else if (status.runtimeType == InProgress) {
int progress = (status as InProgress).progress.round();
// ...
} else if (status.runtimeType == Error) {
// ...
}
};
final OfflineRegion offlineRegion = OfflineRegion(
bounds: LatLngBounds(
northeast: LatLng(52.5050648, 13.3915634),
southwest: LatLng(52.4943073, 13.4055383),
),
id: 1,
minZoom: 6,
maxZoom: 18,
mapStyleUrl: 'mapbox://styles/mapbox/streets-v11',
);
downloadOfflineRegionStream(offlineRegion, onEvent);
The snapshotManager generates static raster images of the map. Each snapshot image depicts a portion of a map defined by an SnapshotOptions object you provide.
takeSnapshot
with predefined SnapshotOptions
final renderBox = mapKey.currentContext?.findRenderObject() as RenderBox;
final snapshotOptions = SnapshotOptions(
width: renderBox.size.width,
height: renderBox.size.height,
writeToDisk: true,
withLogo: false,
);
final uri = await mapController?.takeSnapshot(snapshotOptions);
Add the ACCESS_COARSE_LOCATION
or ACCESS_FINE_LOCATION
permission in the application manifest android/app/src/main/AndroidManifest.xml
to enable location features in an Android application:
<manifest ...
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Starting from Android API level 23 you also need to request it at runtime. This plugin does not handle this for you. The example app uses the flutter 'location' plugin for this.
To enable location features in an iOS application:
If you access your users' location, you should also add the following key to ios/Runner/Info.plist
to explain why you need access to their location data:
xml ...
<key>NSLocationWhenInUseUsageDescription</key>
<string>[Your explanation here]</string>
Recommended explanation about "Shows your location on the map and helps improve the map".
Since Flutter 3.x.x was introduced, it exposed some race conditions resulting in occasional crashes upon map disposal. The parameter useDelayedDisposal
was introduced as a workaround for this issue until Flutter and/or Mapbox fix this issue properly. Use with caution - this is not yet production ready since several users still report crashes after using this workaround.
See the documentation about this topic
We welcome contributions to this repository! If you're interested in helping build this Mapbox-Flutter integration, please read the contribution guide to learn how to get started.
Run this command:
With Flutter:
$ flutter pub add mapbox_gl_platform_interface
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
mapbox_gl_platform_interface: ^0.16.0
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:mapbox_gl_platform_interface/mapbox_gl_platform_interface.dart';
Download Details:
Author: flutter-mapbox-gl
Source Code: https://github.com/flutter-mapbox-gl/maps
1665598800
deck.gl widget for R.
# install latest release
remotes::install_github("qfes/rdeck@*release")
# or install development version
remotes::install_github("qfes/rdeck")
A Mapbox account and mapbox access token is required for Mapbox basemaps, with or without the Mapbox data service. See mapbox_access_token
for usage.
{rdeck}
draws much inspiration from kepler.gl
and {mapdeck}
. {rdeck}
's design choices make it convenient for use in static reports, and certain Shiny usecases that are highly performant.
Some notable differences to {mapdeck}
:
{rmarkdown}
HTML reports made with {rdeck}
are typically an order of magnitude smaller in file size than {mapdeck}
. Thanks to:{ggplot2}
style scale_
functions that perform common layer data transformations and automatically generate legends with appropriate untransformed tick marks.scale_color_power
, scale_color_log
etc.{mapdeck}
at present.Author: qfes
Source Code: https://github.com/qfes/rdeck
License: MIT license
1665431040
An R library which lets you plot large data sets (as much as your GPU & browser can handle), on interactive maps using Mapbox GL and Deck.gl
Mapbox is the location data platform for mobile and web applications. We provide building blocks to add location features like maps, search, and navigation into any experience you create.
deck.gl is a WebGL-powered framework for visual exploratory data analysis of large datasets.
First you need to install it, either from CRAN
install.packages("mapdeck")
Or from github (to get the latest development version)
remotes::install_github("SymbolixAU/mapdeck")
If the github version fails to install, you’ll probably need to update dependencies first
remotes::install_github("dcooley/geometries")
remotes::install_github("dcooley/sfheaders")
remotes::install_github("SymbolixAU/spatialwidget")
Then everything you need to know to get you started is on the home page
Mapdeck uses Mapbox maps, and to use Mapbox you need an access token.
Once you’ve generate a token you can use their maps.
Call mapdeck(token = 'your_token')
to generate a basic map. Then start adding layers by using one of the various add_*()
functions.
url <- 'https://raw.githubusercontent.com/plotly/datasets/master/2011_february_aa_flight_paths.csv'
flights <- read.csv(url)
flights$info <- paste0("<b>",flights$airport1, " - ", flights$airport2, "</b>")
mapdeck(token = key, style = mapdeck_style('dark')) %>%
add_arc(
data = flights
, origin = c("start_lon", "start_lat")
, destination = c("end_lon", "end_lat")
, stroke_from = "airport1"
, stroke_to = "airport2"
, tooltip = "info"
, layer_id = 'arclayer'
)
See the Layers page for more examples
For general help and advice the best place to ask is on StackOverflow (using the mapdeck
tag).
If you’ve found a bug, or want a new feature added then use the issue tracker on github.
I don’t respond to emails asking for help because this is an open source package, and any advice should be kept open so everyone can benefit. (unless you want to pay me!)
Author: SymbolixAU
Source Code: https://github.com/SymbolixAU/mapdeck
1664700909
Flutter Mapbox GL
Please note that this project is community driven and is not an official Mapbox product.
We welcome feedback and contributions.
This Flutter plugin allows to show embedded interactive and customizable vector maps inside a Flutter widget. For the Android and iOS integration, we use mapbox-gl-native. For web, we rely on mapbox-gl-js. This project only supports a subset of the API exposed by these libraries.
This package is available on pub.dev.
Get it by running the following command:
flutter pub add mapbox_gl
A secret access token with the Downloads: Read
scope is required for the underlying Mapbox SDKs to be downloaded. Information on setting it up is available in the Mapbox documentation: Android, iOS.
If the properly configured token is not present, the build process fails with one the following errors (for Android/iOS respectively):
* What went wrong:
A problem occurred evaluating project ':mapbox_gl'.
> SDK Registry token is null. See README.md for more information.
[!] Error installing Mapbox-iOS-SDK
curl: (22) The requested URL returned error: 401 Unauthorized
Include the JavaScript and CSS files in the <head>
of your index.html
file:
<script src='https://api.mapbox.com/mapbox-gl-js/v2.8.2/mapbox-gl.js'></script>
<link href='https://api.mapbox.com/mapbox-gl-js/v2.8.2/mapbox-gl.css' rel='stylesheet' />
<style>
.mapboxgl-map {
position: relative;
width: 100%;
height: 100%;
}
</style>
Note: Look for latest version in Mapbox GL JS documentation.
A public access token must be provided to a MapboxMap widget for retrieving styles and resources. While you can hardcode it directly into source files, it's good practise to retrieve access tokens from some external source (e.g. a config file or an environment variable). The example app uses the following technique:
The access token is passed via the command line arguments when either building
flutter build <platform> --dart-define ACCESS_TOKEN=YOUR_TOKEN_HERE
or running the application
flutter run --dart-define ACCESS_TOKEN=YOUR_TOKEN_HERE
Then it's retrieved in Dart:
MapboxMap(
...
accessToken: const String.fromEnvironment("ACCESS_TOKEN"),
...
)
Feature | Android | iOS | Web |
---|---|---|---|
Style | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Camera | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Gesture | :white_check_mark: | :white_check_mark: | :white_check_mark: |
User Location | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Style DSL | :x: | :x: | :x: |
Raster Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Symbol Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Circle Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Line Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Fill Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Hillshade Layer | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Heatmap Layer | :x: | :x: | :x: |
Vector Source | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Raster Source | :white_check_mark: | :white_check_mark: | :white_check_mark: |
GeoJson Source | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Image Source | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Expressions | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Symbol Annotation | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Circle Annotation | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Line Annotation | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Fill Annotation | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Map styles can be supplied by setting the styleString
in the MapOptions
. The following formats are supported:
MapboxStyles
or a custom map style served remotely using a URL that start with 'http(s)://' or 'mapbox://'assets
and add a reference in pubspec.yml
. Set the style string to the relative path for this asset in order to load it into the map.Support for offline maps is available by side loading the required map tiles and including them in your assets
folder.
Create your tiles package by following the guide available here.
Place the tiles.db file generated in step one in your assets directory and add a reference to it in your pubspec.yml
file.
assets:
- assets/cache.db
installOfflineMapTiles
when your application starts to copy your tiles into the location where Mapbox can access them. NOTE: This method should be called before the Map widget is loaded to prevent collisions when copying the files into place. try {
await installOfflineMapTiles(join("assets", "cache.db"));
} catch (err) {
print(err);
}
An offline region is a defined region of a map that is available for use in conditions with limited or no network connection. Tiles for selected region, style and precision are downloaded from Mapbox using proper SDK methods and stored in application's cache.
Beware of selecting big regions, as size might be significant. Here is an online estimator https://docs.mapbox.com/playground/offline-estimator/.
Call downloadOfflineRegionStream
with predefined OfflineRegion
and optionally track progress in the callback function.
final Function(DownloadRegionStatus event) onEvent = (DownloadRegionStatus status) {
if (status.runtimeType == Success) {
// ...
} else if (status.runtimeType == InProgress) {
int progress = (status as InProgress).progress.round();
// ...
} else if (status.runtimeType == Error) {
// ...
}
};
final OfflineRegion offlineRegion = OfflineRegion(
bounds: LatLngBounds(
northeast: LatLng(52.5050648, 13.3915634),
southwest: LatLng(52.4943073, 13.4055383),
),
id: 1,
minZoom: 6,
maxZoom: 18,
mapStyleUrl: 'mapbox://styles/mapbox/streets-v11',
);
downloadOfflineRegionStream(offlineRegion, onEvent);
The snapshotManager generates static raster images of the map. Each snapshot image depicts a portion of a map defined by an SnapshotOptions object you provide.
takeSnapshot
with predefined SnapshotOptions
final renderBox = mapKey.currentContext?.findRenderObject() as RenderBox;
final snapshotOptions = SnapshotOptions(
width: renderBox.size.width,
height: renderBox.size.height,
writeToDisk: true,
withLogo: false,
);
final uri = await mapController?.takeSnapshot(snapshotOptions);
Add the ACCESS_COARSE_LOCATION
or ACCESS_FINE_LOCATION
permission in the application manifest android/app/src/main/AndroidManifest.xml
to enable location features in an Android application:
<manifest ...
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Starting from Android API level 23 you also need to request it at runtime. This plugin does not handle this for you. The example app uses the flutter 'location' plugin for this.
To enable location features in an iOS application:
If you access your users' location, you should also add the following key to ios/Runner/Info.plist
to explain why you need access to their location data:
xml ...
<key>NSLocationWhenInUseUsageDescription</key>
<string>[Your explanation here]</string>
Recommended explanation about "Shows your location on the map and helps improve the map".
See the documentation about this topic
We welcome contributions to this repository! If you're interested in helping build this Mapbox-Flutter integration, please read the contribution guide to learn how to get started.
Run this command:
With Flutter:
$ flutter pub add vtmap_gl_platform_interface
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
vtmap_gl_platform_interface: ^0.0.8
Alternatively, your editor might support flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:vtmap_gl_platform_interface/vtmap_gl_platform_interface.dart';
Download Details:
Author: flutter-mapbox-gl
Source Code: https://github.com/flutter-mapbox-gl/maps
1658886660
Preview geojson locally using Electron and Mapbox GL
Install:
npm install -g geojsonapp
Run:
geojsonapp
Open file:
geojsonapp filename.geojson
cat filename.geojson | geojsonapp
Author: Mick
Source Code: https://github.com/mick/geojsonapp
License:
1657254780
Desktop application for vector tile driven map design.
Install node v0.10.x. Then
git clone https://github.com/mapbox/mapbox-studio-classic.git
cd mapbox-studio-classic
npm install
npm start
Mapbox Studio Classic ships with pre-built binaries for common platforms:
The minimum platforms versions are:
Ubuntu 12.04 (Precise) can be supported by upgrading libstdc++:
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt-get update -q
sudo apt-get install -y libstdc++6
If packages like node-mapnik
fail to install then you are likely running a platform for which no binaries are available. In this case you will need to build these packages from source (Feel free to create a github issue to ask for help).
You can do this like:
npm install --build-from-source
The docs offer both a step-by-step guide to creating your first projects in Mapbox Studio Classic and detailed information about styling and creating vector sources.
Build status of modules:
Author: Mapbox
Source Code: https://github.com/mapbox/mapbox-studio-classic
License: BSD-3-Clause license
1647101760
Flutter Mapbox GL Native
This Flutter plugin for mapbox-gl-native enables embedded interactive and customizable vector maps inside of a Flutter widget. This project plugin is in early development stage. Only Android is supported for now.
Following examples use Mapbox vector tiles, which require a Mapbox account and a Mapbox access token. Obtain a free access token on your Mapbox account page. After you get the key, place it in project's Android directory:
local.properties
file with the following path: $project_dir/android/local.properties
mapbox.accessToken="YOUR MAPBOX ACCESS TOKEN"
token to the local.properties file.flutter doctor
git clone git@github.com:mapbox/flutter-mapbox-gl.git
cd flutter_mapbox/example && flutter run
mapbox_gl: ^0.0.1
dependency to pubspec.yaml
file and get the packageandroid/app/src/main/AndroidManifest.xml
:<manifest ...
<application ...
<meta-data android:name="com.mapbox.token" android:value="YOUR_TOKEN_HERE" />
import 'package:mapbox_gl/mapbox_gl.dart';
This README file currently houses all of the documentation for this Flutter project. Please visit mapbox.com/android-docs if you'd like more information about the Mapbox Maps SDK for Android and mapbox.com/ios-sdk for more information about the Mapbox Maps SDK for iOS.
This repository's example library is currently the best place for you to find reference code for this project.
We welcome contributions to this repository!
If you're interested in helping build this Mapbox/Flutter integration, please read the contribution guide to learn how to get started.
Please note that this project has moved. Please head to https://github.com/tobrun/flutter-mapbox-gl for updates.
Author: Mapbox
Source Code: https://github.com/mapbox/flutter-mapbox-gl
License: View license
1632843978
Mapbox Flutter
This project is inspired by Mapbox_gl
This project also integrates the Null_Safety
We welcome feedback and contributions.
flutter doctor
git clone git@github.com:BorisGautier/mapbox_flutter.git
flutter devices
cd mapbox_flutter/example && flutter packages get && flutter run -d {device_id}
This project uses Mapbox vector tiles, which requires a Mapbox account and a Mapbox access token. Obtain a free access token on your Mapbox account page.
Even if you do not use Mapbox vector tiles but vector tiles from a different source (like self-hosted tiles) with this plugin, you will need to specify any non-empty string as Access Token as explained below!
The recommended way to provide your access token is through the accessToken
parameter of the MapboxFlutterMap
constructor Note that you should always use the same token throughout your application.
Adding a Mapbox Access Token
This project uses Mapbox vector tiles, which requires a Mapbox account and a Mapbox access token. Obtain a free access token on your Mapbox account page.
Even if you do not use Mapbox vector tiles but vector tiles from a different source (like self-hosted tiles) with this plugin, you will need to specify any non-empty string as Access Token as explained below!
The recommended way to provide your access token is through the MapboxFlutterMap
constructor's accessToken
parameter. Note that you should always use the same token throughout your entire app.
For earlier versions you need to set your access token through platform-specific files as described below. This will also continue to work as a fallback on v0.8. You should not set the access token through both the constructor parameter and platform-specific files!
Add Mapbox access token configuration in the application manifest example/android/app/src/main/AndroidManifest.xml
:
<manifest ...
<application ...
<meta-data android:name="com.mapbox.token" android:value="YOUR_TOKEN_HERE" />
Add Mapbox access token configuration to the application Info.plist example/ios/Runner/Info.plist
:
<key>io.flutter.embedded_views_preview</key>
<true/>
<key>MGLMapboxAccessToken</key>
<string>YOUR_TOKEN_HERE</string>
Add Mapbox access token configuration to index.html example/web/index.html
:
<body>
...
<script>
mapboxgl.accessToken = 'YOUR_TOKEN_HERE';
</script>
</body>
You must also [configure a secret access token having the Download: read scope][https://docs.mapbox.com/ios/maps/guides/install/]. If this configuration is not present, an error like the following appears during the iOS build.
[!] Error installing Mapbox-iOS-SDK
curl: (22) The requested URL returned error: 401 Unauthorized
Update buildTypes in android\app\build.gradle
buildTypes {
release {
// other configs
ndk {
abiFilters 'armeabi-v7a','arm64-v8a','x86_64', 'x86'
}
}
}
This project is available on pub.dev, follow the instructions to integrate a package into your flutter application. For platform specific integration, use the flutter application under the example folder as reference.
Feature | Android | iOS | Web |
---|---|---|---|
Style | ✅ | ✅ | ✅ |
Camera | ✅ | ✅ | ✅ |
Gesture | ✅ | ✅ | ✅ |
User Location | ✅ | ✅ | ✅ |
Symbol | ✅ | ✅ | ✅ |
Circle | ✅ | ✅ | ✅ |
Line | ✅ | ✅ | ✅ |
Fill | ✅ | ✅ | ✅ |
Map styles can be supplied by setting the styleString
in the MapOptions
. The following formats are supported:
MapboxStyles
or a custom map style served remotely using a URL that start with 'http(s)://' or 'mapbox://'assets
and add a reference in pubspec.yml
. Set the style string to the relative path for this asset in order to load it into the map.Support for offline maps is available by "side loading" the required map tiles and including them in your assets
folder.
Create your tiles package by following the guide available here.
Place the tiles.db file generated in step one in your assets directory and add a reference to it in your pubspec.yml
file.
assets:
- assets/cache.db
installOfflineMapTiles
when your application starts to copy your tiles into the location where Mapbox can access them. NOTE: This method should be called before the Map widget is loaded to prevent collisions when copying the files into place. try {
await installOfflineMapTiles(join("assets", "cache.db"));
} catch (err) {
print(err);
}
An offline region is a defined region of a map that is available for use in conditions with limited or no network connection. Tiles for selected region, style and precision are downloaded from Mapbox using proper SDK methods and stored in application's cache.
Beware of selecting big regions, as size might be significant. Here is an online estimator https://docs.mapbox.com/playground/offline-estimator/.
Call downloadOfflineRegionStream
with predefined OfflineRegion
and optionally track progress in the callback function.
final Function(DownloadRegionStatus event) onEvent = (DownloadRegionStatus status) {
if (status.runtimeType == Success) {
// ...
} else if (status.runtimeType == InProgress) {
int progress = (status as InProgress).progress.round();
// ...
} else if (status.runtimeType == Error) {
// ...
}
};
final OfflineRegion offlineRegion = OfflineRegion(
bounds: LatLngBounds(
northeast: LatLng(52.5050648, 13.3915634),
southwest: LatLng(52.4943073, 13.4055383),
),
id: 1,
minZoom: 6,
maxZoom: 18,
mapStyleUrl: 'mapbox://styles/mapbox/streets-v11',
);
downloadOfflineRegionStream(offlineRegion, onEvent);
Add the ACCESS_COARSE_LOCATION
or ACCESS_FINE_LOCATION
permission in the application manifest android/app/src/main/AndroidManifest.xml
to enable location features in an Android application:
<manifest ...
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Starting from Android API level 23 you also need to request it at runtime. This plugin does not handle this for you. The example app uses the flutter 'location' plugin for this.
To enable location features in an iOS application:
If you access your users' location, you should also add the following key to ios/Runner/Info.plist
to explain why you need access to their location data:
xml ...
<key>NSLocationWhenInUseUsageDescription</key>
<string>[Your explanation here]</string>
Mapbox recommends the explanation "Shows your location on the map and helps improve the map".
dev
branch. Add nice description in PR.Run this command:
With Flutter:
$ flutter pub add mapbox_flutter
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
mapbox_flutter: ^1.0.0-dev.1.1
Alternatively, your editor might support or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:mapbox_flutter/mapbox_flutter.dart';
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:location/location.dart';
import 'package:mapbox_flutter_example/custom_marker.dart';
import 'package:mapbox_flutter_example/offline_regions.dart';
import 'package:mapbox_flutter_example/place_batch.dart';
import 'animate_camera.dart';
import 'annotation_order_maps.dart';
import 'full_map.dart';
import 'line.dart';
import 'local_style.dart';
import 'map_ui.dart';
import 'move_camera.dart';
import 'page.dart';
import 'place_circle.dart';
import 'place_source.dart';
import 'place_symbol.dart';
import 'place_fill.dart';
import 'scrolling_map.dart';
final List<ExamplePage> _allPages = <ExamplePage>[
MapUiPage(),
FullMapPage(),
AnimateCameraPage(),
MoveCameraPage(),
PlaceSymbolPage(),
PlaceSourcePage(),
LinePage(),
LocalStylePage(),
PlaceCirclePage(),
PlaceFillPage(),
ScrollingMapPage(),
OfflineRegionsPage(),
AnnotationOrderPage(),
CustomMarkerPage(),
BatchAddPage(),
];
class MapsDemo extends StatelessWidget {
//FIXME: Add your Mapbox access token here
static const String ACCESS_TOKEN = "YOUR_TOKEN_HERE";
void _pushPage(BuildContext context, ExamplePage page) async {
if (!kIsWeb) {
final location = Location();
final hasPermissions = await location.hasPermission();
if (hasPermissions != PermissionStatus.granted) {
await location.requestPermission();
}
}
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (_) => Scaffold(
appBar: AppBar(title: Text(page.title)),
body: page,
)));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('MapboxFlutterMaps examples')),
body: ListView.builder(
itemCount: _allPages.length,
itemBuilder: (_, int index) => ListTile(
leading: _allPages[index].leading,
title: Text(_allPages[index].title),
onTap: () => _pushPage(context, _allPages[index]),
),
),
);
}
}
void main() {
runApp(MaterialApp(home: MapsDemo()));
}
Download Details:
Author: BorisGautier
Source Code: https://github.com/BorisGautier/mapbox_flutter
1632403554
Flutter VTMaps GL
This Flutter plugin allows to show embedded interactive and customizable vector maps inside a Flutter widget. For the Android and iOS integration, we use mapbox-gl-native. For web, we rely on mapbox-gl-js. This project only supports a subset of the API exposed by these libraries.
flutter doctor
git clone git@github.com:tobrun/flutter-mapbox-gl.git
flutter devices
cd flutter_mapbox/example && flutter packages get && flutter run -d {device_id}
This project uses VTMaps vector tiles, which requires a VTMaps account and a VTMaps access token. Obtain a free access token on your VTMaps account page.
Even if you do not use VTMaps vector tiles but vector tiles from a different source (like self-hosted tiles) with this plugin, you will need to specify any non-empty string as Access Token as explained below!
The recommended way to provide your access token is through the VTMaps
constructor's accessToken
parameter, which is available starting from the v0.8 release. Note that you should always use the same token throughout your entire app.
An alternative method to provide access tokens that was required until the v0.7 release is described in this wiki article.
This project is available on pub.dev, follow the instructions to integrate a package into your flutter application. For platform specific integration, use the flutter application under the example folder as reference.
Feature | Android | iOS | Web |
---|---|---|---|
Style | ✅ | ✅ | ✅ |
Camera | ✅ | ✅ | ✅ |
Gesture | ✅ | ✅ | ✅ |
User Location | ✅ | ✅ | ✅ |
Symbol | ✅ | ✅ | ✅ |
Circle | ✅ | ✅ | ✅ |
Line | ✅ | ✅ | ✅ |
Fill | ✅ | ✅ | ✅ |
Map styles can be supplied by setting the styleString
in the MapOptions
. The following formats are supported:
MapboxStyles
or a custom map style served remotely using a URL that start with 'http(s)://'assets
and add a reference in pubspec.yml
. Set the style string to the relative path for this asset in order to load it into the map.Support for offline maps is available by "side loading" the required map tiles and including them in your assets
folder.
Create your tiles package by following the guide available here.
Place the tiles.db file generated in step one in your assets directory and add a reference to it in your pubspec.yml
file.
assets:
- assets/cache.db
installOfflineMapTiles
when your application starts to copy your tiles into the location where Mapbox can access them. NOTE: This method should be called before the Map widget is loaded to prevent collisions when copying the files into place. try {
await installOfflineMapTiles(join("assets", "cache.db"));
} catch (err) {
print(err);
}
To enable location features in an Android application:
You need to declare the ACCESS_COARSE_LOCATION
or ACCESS_FINE_LOCATION
permission in the AndroidManifest.xml and starting from Android API level 23 also request it at runtime. The plugin does not handle this for you. The example app uses the flutter 'location' plugin for this.
To enable location features in an iOS application:
If you access your users' location, you should also add the following key to your Info.plist to explain why you need access to their location data:
<key>NSLocationWhenInUseUsageDescription</key>
<string>[Your explanation here]</string>
Mapbox recommends the explanation "Shows your location on the map and helps improve the map".
This README file currently houses all of the documentation for this Flutter project. Please visit mapbox.com/android-docs if you'd like more information about the Mapbox Maps SDK for Android and mapbox.com/ios-sdk for more information about the Mapbox Maps SDK for iOS.
We welcome contributions to this repository! If you're interested in helping build this Mapbox/Flutter integration, please read the contribution guide to learn how to get started.
Run this command:
With Flutter:
$ flutter pub add vtmap_gl
This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get
):
dependencies:
vtmap_gl: ^1.1.13
Alternatively, your editor might support or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:vtmap_gl/vtmap_gl.dart';
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:location/location.dart';
import 'package:mapbox_gl_example/full_map.dart';
import 'animate_camera.dart';
import 'full_map.dart';
import 'line.dart';
import 'map_ui.dart';
import 'move_camera.dart';
import 'page.dart';
import 'place_circle.dart';
import 'place_source.dart';
import 'place_symbol.dart';
import 'place_fill.dart';
import 'scrolling_map.dart';
final List<ExamplePage> _allPages = <ExamplePage>[
MapUiPage(),
FullMapPage(),
AnimateCameraPage(),
MoveCameraPage(),
PlaceSymbolPage(),
PlaceSourcePage(),
LinePage(),
PlaceCirclePage(),
PlaceFillPage(),
ScrollingMapPage(),
];
class MapsDemo extends StatelessWidget {
//FIXME: Add your Mapbox access token here
// static const String ACCESS_TOKEN = "pk.eyJ1IjoibGVwcHJvOTAiLCJhIjoiY2s4cG94N2hpMDU4MTNlcGdvY2gyamQwayJ9.LrwJ0gNZ6ncU7yLeaHKxAQ";
static const String ACCESS_TOKEN = "6ht5fdbc-1996-4f54-87gf-5664f304f3d2";
void _pushPage(BuildContext context, ExamplePage page) async {
if (!kIsWeb) {
final location = Location();
final hasPermissions = await location.hasPermission();
if (hasPermissions != PermissionStatus.GRANTED) {
await location.requestPermission();
}
}
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (_) => Scaffold(
appBar: AppBar(title: Text(page.title)),
body: page,
)));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('MapboxMaps examples')),
body: ListView.builder(
itemCount: _allPages.length,
itemBuilder: (_, int index) => ListTile(
leading: _allPages[index].leading,
title: Text(_allPages[index].title),
onTap: () => _pushPage(context, _allPages[index])),
),
);
}
}
void main() {
runApp(MaterialApp(home: MapsDemo()));
}
Download Details:
Author: tobrun
Source Code: https://github.com/tobrun/flutter-mapbox-gl
1632130928
About
This package provides easy api calls to MapBox Search API.
Also, it contains an static map image generator 😆.
Maki Icons can be used now in marker icon
As this package doesn't depend on Flutter SDK you cannot use the Colors
class anymore on you Flutter project. Instead use map_box_search's Color.rgb
constructor, passing the values as parameters:
var myColor = Colors.redAccent; //Flutter's Color class
var mapBoxColor = Color.rgb( myColor.red, myColor.green, myColor.blue);
First of all you must acquire an API key on the MapBox website https://www.mapbox.com/
Then, add the following to your pubspec.yaml
file:
dependencies:
mapbox_search: any
Examples
var reverseGeoCoding = ReverseGeoCoding(
apiKey: 'API Key',
limit: 5,
);
Future<List<MapBoxPlace>> getPlaces() =>
reverseGeoCoding.getAddress(
Location(lat: 72.0, lng: 76.00),
);
var placesSearch = PlacesSearch(
apiKey: 'API Key',
limit: 5,
);
Future<List<MapBoxPlace>> getPlaces() =>
placesSearch.getPlaces("New York");
MapBoxStaticImage staticImage = MapBoxStaticImage(
apiKey:
"API Key");
String getStaticImageWithPolyline() => staticImage.getStaticUrlWithPolyline(
point1: Location(lat: 37.77343, lng: -122.46589),
point2: Location(lat: 37.75965, lng: -122.42816),
marker1: MapBoxMarker( markerColor: Colors.black,
markerLetter: MakiIcons.aerialway.value,
markerSize: MarkerSize.LARGE),
marker2: MapBoxMarker(
markerColor: Color.rgb(244, 67, 54),
markerLetter: 'q',
markerSize: MarkerSize.SMALL),
height: 300,
width: 600,
zoomLevel: 16,
style: MapBoxStyle.Mapbox_Dark,
path: MapBoxPath(pathColor: Color.rgb(255, 0, 0), pathOpacity: 0.5, pathWidth: 5),
render2x: true);
String getStaticImageWithMarker() => staticImage.getStaticUrlWithMarker(
center: Location(lat: 37.77343, lng: -122.46589),
marker: MapBoxMarker(
markerColor: Color.rgb(0, 0, 0), markerLetter: 'p', markerSize: MarkerSize.LARGE),
height: 300,
width: 600,
zoomLevel: 16,
style: MapBoxStyle.Mapbox_Streets,
render2x: true,
);
String getStaticImageWithoutMarker() => staticImage.getStaticUrlWithoutMarker(
center: Location(lat: 37.75965, lng: -122.42816),
height: 300,
width: 600,
zoomLevel: 16,
style: MapBoxStyle.Mapbox_Outdoors,
render2x: true,
);
Screenshots
Run this command:
With Dart:
$ dart pub add mapbox_search
With Flutter:
$ flutter pub add mapbox_search
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get
):
dependencies:
mapbox_search: ^3.0.1+1
Alternatively, your editor might support dart pub get
or flutter pub get
. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:mapbox_search/mapbox_search.dart';
import 'package:mapbox_search/mapbox_search.dart';
// import 'credentials.dart';
final MAPBOX_KEY = '';
Future<void> main() async {
String apiKey = MAPBOX_KEY; //Set up a test api key before running
await geoCoding(apiKey).catchError(print);
await placesSearch(apiKey).catchError(print);
}
///Reverse GeoCoding sample call
Future geoCoding(String apiKey) async {
var geoCodingService = ReverseGeoCoding(
apiKey: apiKey,
country: "BR",
limit: 5,
);
var addresses = await geoCodingService.getAddress(Location(
lat: -19.984846,
lng: -43.946852,
));
print(addresses);
}
///Places search sample call
Future placesSearch(String apiKey) async {
var placesService = PlacesSearch(
apiKey: apiKey,
country: "BR",
limit: 5,
);
var places = await placesService.getPlaces(
"patio",
location: Location(
lat: -19.984634,
lng: -43.9502958,
),
);
print(places);
}
Download Details:
Author: ketanchoyal
Source Code: https://github.com/ketanchoyal/mapbox_search