Avoid coordinates when generating a route using Mapbox Direction API - ios

I have been using Mapbox for my app to generate route and turn-by-turn navigation and it's working well. However I would like to avoid to go through some coordinates of the route but I can't figure it out.
The code to get the route :
Directions.shared.calculate(options) { [unowned self] (waypoints, routes, error) in
// Take first route and customize it in a way to get around some coordinates
}
Here is a scenario :
1- User location is latitude = 37.332331410000002, longitude = -122.0312186
2- The user is going to Santa Clara Unified School located on latitude = 37.354100000000003,longitude = -121.9552
3- The Api generates the following route :
[0] = {
latitude = 37.332329999999999
longitude = -122.03118000000001
}
[1] = {
latitude = 37.332619999999999
longitude = -122.03118000000001
}
[2] = {
latitude = 37.332609999999995
longitude = -122.03097000000001
}
[3] = {
latitude = 37.332609999999995
longitude = -122.03076000000001
}
[4] = {
latitude = 37.332199999999993
longitude = -122.03076000000001
}
[5] = {
latitude = 37.331689999999995
longitude = -122.03076000000001
}
[6] = {
latitude = 37.331689999999995
longitude = -122.03190000000002
}
[7] = {
latitude = 37.331719999999997
longitude = -122.03199000000002
}
[8] = {
latitude = 37.331759999999996
longitude = -122.03205000000003
} ...
4- Suppose the generated route goes through East Homestead Rd, I would like to be able to avoid this road and generate a new route even if it's a longer one.In the screen below avoid the route in red because going through East Homestead Rd and take the next fastest route not going through East Homestead Rd
Any help would be appreciated !
EDIT : Here is the query for finding if a route has points to avoid in it
// $linestring is the array of coordinates from the route in the string format of (lng lat,lng2 lat2,lng3 lat3,lng4 lat4....)
$query = $this->em->createQuery('
SELECT count(i) as counter
FROM HitsBundle:Hit i
WHERE i.datetime BETWEEN :lastMonth AND :now
AND
MBRCovers(
ST_Buffer(
ST_GeomFromText(\'LineString('.$linestring.')\') ,
0.00001
),
i.coordinates
) = 1
GROUP BY i.coordinates
HAVING counter > 1
')
->setParameter('lastMonth', $lastMonth)
->setParameter('now', new \DateTime())
->setMaxResults(1);
EDIT: Related issue on Github

I may be rough-guessing here, but looking through Mapbox API it does not have any options to avoid while generating routes, therefore you need to implement some route-selection logic on client-side.
Basically you need to have an algorithm which gets a set of points to avoid and checks if your Route geometry GeoJSON or Polyline are within some threshold range from given points. If it is - discard the route (or lower route priority).
Of course it may fail to find a route if all routes provided by Mapbox are discarded - Mapbox does not know about your restrictions, therefore using weight for routes could be one option of solving this.
These posts might give you some hints:
Is it possible to determine if a GeoJSON point is inside a GeoJSON polygon using JavasScript
How to check if a Latitude/Longitude point is on the GRoute in Google Maps API

After few months dealing with the MapBox Direction API we've come to the conclusion that it's not reliable for this specific use case. When calculating routes from Point A to Point B using the Direction API, MapBox offers an option includesAlternativeRoutes if set to true it provides alternative routes. However this is not consistent and in most cases it returns only the preferred route.
According to MapBox :
If the value of this property is true, the server attempts to find additional reasonable routes that visit the waypoints. Regardless, multiple routes are only returned if it is possible to visit the waypoints by a different route without significantly increasing the distance or travel time.
So we will switch to Google Maps as this feature is crucial to our business logic.

Related

Migrating to Places 4.0 SDK, how to migrate place Bias?

I am migrating to the new places SDK 4.0
The old func was:
findAutocompletePredictions(fromQuery: query,
bounds: bounds,
boundsMode: .bias,
filter: filter,
sessionToken: token) { (prediction, error) in
callback(prediction, error)
}
the new func as per docs is
findAutocompletePredictions(fromQuery: query,
filter: filter,
sessionToken: token) { (prediction, error) in
callback(prediction, error)
}
I see that bounds bias is now set on the GMSAutocompleteFilter filter
This has a locationBias property where i set some and it takes a parameter of <GMSPlaceLocationBias>
The issue i have is there is no info on what to provide here...GMSPlaceLocationBias is a protocol, and i have no idea what im meant to use object wise to provide the old bounds data.
Conforming to the protocol gives me a locationBiasURLQueryItem fun to populate, with no further documentation on what should go in this function
So im stuck and cant upgrade as there doesnt seem to be a clear way forward.
Anyone have any ideas? Docs are here: https://developers.google.com/places/ios-sdk/reference/interface_g_m_s_places_client#ab60773feec0e68751c5a7f1066b9d252)
My attempt to construct the filter is:
let filter = GMSAutocompleteFilter()
if case .location(let location) = _locationController.locationStatus {
filter.locationBias = .some(location)
}
But obviously my custom location model object doesnt confirm to GMSPlaceLocationBias and is a struct so cant be used and errors
I got totally lost on this as well, but just found an example buried in one of the guides here.
In short the implementation looks like this:
let filter = GMSAutocompleteFilter()
filter.locationBias = GMSPlaceRectangularLocationOption(bounds.northEast, bounds.southWest)
Here I'm using the same bounds I had previously been using in 3.x (in my case from the map currently being displayed) to get those northEast and southWest coordinates, but you can generate your own based on how where you are trying to bias results.
I wish they had just made ANY indication in their docs that this was the intended way to do it.

MapKit Define the desired type of search results (Country, city, region, etc)

For an app i'm building, I want to implement a feature that allows users to specify the geographical origin of wines (country (e.g. France), region (e.g. Bordeaux), subregion (e.g. Paullac)).
I want to make sure that I don't have to add all available countries myself, and that all information that comes into the database is valid. Therefore, I decided to do it as follows:
User adds a new wine and types the name of the country it comes from
While typing, the app searches in the apple maps database
The results from this search get displayed as suggestions, and when the user taps a suggestion, the app creates a Country object with all relevant information. The wine van only be saved when such an object is present
This works fine, except one thing: Apple maps returns anything, like restaurants, shops, etcetera, from anywhere.
My question: How can I specify WHAT I am looking for? I can only specify the region I'm searching in, which is irrelevant in my case. I would like to be able to tell apple maps to ONLY look for countries, regions, cities, whatever. Is this possible in a way? I have exhausted google for this and found no way thus far.
Going off what #Trevor said, I found rejecting results where either the title or subtitle have numbers yields pretty good results if you only want cities and towns.
Swift 4.1 code:
// Store this as a property if you're searching a lot.
let digitsCharacterSet = NSCharacterSet.decimalDigits
let filteredResults = completer.results.filter { result in
if result.title.rangeOfCharacter(from: digitsCharacterSet) != nil {
return false
}
if result.subtitle.rangeOfCharacter(from: digitsCharacterSet) != nil {
return false
}
return true
}
or more compactly:
let filteredResults = completer.results.filter({ $0.title.rangeOfCharacter(from: digitsCharacterSet) == nil && $0.subtitle.rangeOfCharacter(from: digitsCharacterSet) == nil })
The best solution we found was to filter our results using a comma in the result's title. This mostly returned only results that matched a city's format, e.g Detroit, MI, United States. We added this filter to the ones suggested by #Ben Stahl. Ben's solution filtered out edge cases where a comma formed part of the business' name.
This usually returns the correct result within three characters. To answer the OP's question, you could then parse this string by city, state or country to get the desired result.
For better results you could use the Google Places API.
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
self.searchResults = completer.results.filter { result in
if !result.title.contains(",") {
return false
}
if result.title.rangeOfCharacter(from: CharacterSet.decimalDigits) != nil {
return false
}
if result.subtitle.rangeOfCharacter(from: CharacterSet.decimalDigits) != nil {
return false
}
return true
}
self.searchResultsCollectionView.reloadData()
}
I have worked with MapKit and don't believe you can do autocomplete assistance on user entries as they type the best solution I found is Google Place API autocomplete
iOS right now provides receiving geo-coordinates when sending a well-formatted address , or you can receive an address when sending a pair of coordinates. Or points of interest for locations names or coordinates.
There was a class added to MapKit in iOS 9.3 called MKLocalSearchCompleter which helps with autocompletion. You can filter what is returned by using 'MKSearchCompletionFilterType' but that isn't the most extensive and might not fully help with your situation. It does return cities and countries as results but it also returns businesses when I've used it.
One possible option is to filter the returned results again on the app side and exclude all results that have a numeric character in them.
func setupCompleter() {
self.searchCompleter = MKLocalSearchCompleter()
self.searchCompleter?.delegate = self
self.searchCompleter?.filterType = .locationsOnly
self.searchCompleter?.queryFragment = "Bordeaux"
}
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
print("Results \(completer.results)")
// Do additional filtering on results here
}
In addition to Allan's answer, I've found that if you filter by the subtitle property of a MkLocalSearchCompletion object, you can remove the business entries.

iOS - Build polyline from Google Maps Directions API response

How can I get a polyline out of the Directions API JSON response?
Using the overview_polyline is not an option as it is only a smoothened version of the actual polyline.
I also tried getting the start and end location coordinates from the steps, however those will only give me a very inaccurate polyline, because they only contain a few coordiantes.
How can I get an accurate polyline out of the Directions response?
EDIT: Using #SaxonDruce answer, I can get accurate polylines however, they look like this:
Based on the sample response, you'll need something equivalent to:
response.routes[0].legs[0].steps[i].polyline.points
So, get the first route, then the first leg, then loop over the steps, then get the polyline, then get the points.
To add to saxon druce's answer, You can loop over all the steps of first leg and combine all the points to form a polyline.
List<LatLng> points = new ArrayList<>();
for (DirectionsStep step : legs[i].steps) {
points.addAll(step.polyline.decodePath());
}
EncodedPolyline legPolyline = new EncodedPolyline(points);

Cannot to get twitter data in specific location

I try to get American tweets using Twitter4J, when I set the specific location like this code:
FilterQuery fq = new FilterQuery();
double lat1 = 48.996059d;
double long1 = -125.361695d;
double lat2 = 29.269125d;
double long2 = -67.683473d;
twitterStream.addListener(listener);
double[][] bb = {{lat1, long1}, {lat2, long2}};
fq.locations(bb);
twitterStream.filter(fq);
I got error like this:
Parameter not accepted with the role. 406:Returned by the Search API when an invalid format is specified in the request.
Returned by the Streaming API when one or more of the parameters are not suitable for the resource. The track parameter, for example, would throw this error if:
The track keyword is too long or too short.
The bounding box specified is invalid.
No predicates defined for filtered resource, for example, neither track nor follow parameter defined.
Follow userid cannot be read.
Latitude/longitude are not valid: 49.00, -125.36, 29.27, -67.68
I guess Twitter4J cannot read minus(-) coordinate, because when I set without minus, it works.
Thank you for any reply
I solve it by self. It must SW first

How can i get all SKMap location objects so I can filter trough them?

I use SKMap in a offline navigation iOS application and:
I'm trying to search from a UITextField a street/city/district within a preinstalled map and I want to get all objects that matches my search.
How can I get all map location objects so I can filter trough them?
From the sample didn't quite managed to do so.(I get only cities)
Is there other alternative or I must loop trough every city and get streets (Seems ugly)
You have to use SKNearbySearchSettings class and to set
searchObject.searchType = SKAll;
A more detailed example of using this class (should return all streets and POIs containing the "pizza" keyword):
SKNearbySearchSettings *searchObject = [SKNearbySearchSettings nearbySearchSettings];
searchObject.coordinate = CLLocationCoordinate2DMake(52.5233, 13.4127);
searchObject.radius = 40000;
searchObject.searchMode = SKSearchHybrid;
searchObject.searchResultSortType = SKMatchSort;
searchObject.searchType = SKAll;
searchObject.searchTerm = "pizza";
[[SKSearchService sharedInstance]startNearbySearchWithSettings:searchObject];

Resources