Is it possible to get a list of places near a Lat/Long using MapKit? - mapkit

I'm trying to build a simple location search that returns a list of places near the user. So for example, using current location, or a given Latitude and Longitude I want to list out all place names, businesses, towns, etc. within a certain radius. Is this possible using MapKit? I know it's easy to do using Facebook graph API.
The best I can find is to do CLGeocoder().reverseGeocodeLocation but that just returns the nearest address. I even tried using
CLLocation(
coordinate:CLLocationCoordinate2D,
altitude:CLLocationDistance,
horizontalAccuracy:CLLocationAccuracy,
verticalAccuracy:CLLocationAccuracy,
timestamp:Date
)
and setting the accuracy to 1,000 meters, but that didn't increase the number of results either. So, my assumption is that with MapKit, you can not get a list of place results around a given point. Is that true?
For good measure, here is the code I'm calling.
let latitude = CLLocationDegrees(40.692001)
let longitude = CLLocationDegrees(-73.983386)
let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let altitude = CLLocationDistance(1000.0)
let accuracy = CLLocationAccuracy(1000.0)
let locationArea = CLLocation(coordinate: location, altitude: altitude, horizontalAccuracy: accuracy, verticalAccuracy: accuracy, timestamp: Date())
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(locationArea) { (placemarks, error) in
if error == nil {
print(placemarks!.count)
for place in placemarks! {
print(place)
}
} else {
print(error!)
}
}

Unless the API has changed, I believe the answer is no. I ended up using the facebook places API.

Related

How to read values from a view to insert in ratingValue and ratingCount in my web in order to be recognized by Google Structured data testing tool?

I was using an external service to get Aggregate Rating in my recipes blog, but dis service disappeared so I decided to build one myself. First of all, this is my first experience with cloud data and JavaScript programming so please, be paciente with me :-).
I'm doing my experiments in this duplicate of my blog: https://jleavalc.blogspot.com/
by now it works as I planned, letting one to vote and storing results in a oracle table, making it possible to retrieve results from a view of this table to get ratingCount and ratingValue values, as anyone can see in that link...
But at the end, despite you can see the stars, despite you can vote and get result stored, showing voting results, Structured data testing tool don't see tag values, so all work is useless.
I think I'm getting close to the problem, but not getting close to the solution. I have the impression that the cause of my problems is the asynchrony of the execution of the script that brings the data from the table, while the function is executed, the browser continues to render the page and it doesn't arrive in time to write those values ​​before the google tool can read them, so they appear empty to it.
I have tried everything including labels and variables in GTM with the same result. The latest version of the code, from this morning is installed right before the "/head" tag and it looks like this:
<script style='text/javascript'>
var myPostId = "<data:widgets.Blog.first.posts.first.id/>";
// <![CDATA[
var micuenta = 0;
var nota = 0;
getText("https://ge4e65cc87f573d-XXXXXXXXXXXX.adb.eu-amsterdam-1.oraclecloudapps.com/ords/admin/notas/?q={\"receta\":{\"$eq\":\"" + myPostId + "\"}}");
async function getText(file) {
let x = await fetch(file);
let y = await x.text();
let datos = JSON.parse(y);
nota = datos.items[0].media;
micuenta = datos.items[0].votos;
};
// This version gives the same result and is interchangeable with the previous one. I keep it commented so as not to forget it:
// var settings = {
// "url": "https://ge4e65cc87f573d-db20220526112405.adb.eu-amsterdam-1.oraclecloudapps.com/ords/admin/notas/?q={\"receta\":{\"$eq\":\"" + myPostId + "\"}}",
// "method": "GET",
// "timeout": 0,
// "async": false,
// };
// $.ajax(settings).done(function (response) {
// if (response.items.length != 0) {
// micuenta = response.items[0].votos;
// nota = response.items[0].media;
// }
// });
</script>
The key is, I think, getting this call to execute before Google's tool finishes rendering the Blogger post page.
The URL that I invoke to get the data calls an oracle view that returns a single row with the corresponding data from the recipe, placing this call:
recipe
In the browser the result is the following:
{"items":[{"receta":"5086941171011962392","media":4.5,"votos":12}],"hasMore":false,"limit":25,"offset":0,"count":1,"links":[{"rel":"self","href":"https://ge4e65cc87f573d-db20220526112405.adb.eu-amsterdam-1.oraclecloudapps.com/ords/admin/notas/?q=%7B%22receta%22:%7B%22%24eq%22:%225086941171011962392%22%7D%7D"},{"rel":"edit","href":"https://ge4e65cc87f573d-db20220526112405.adb.eu-amsterdam-1.oraclecloudapps.com/ords/admin/notas/?q=%7B%22receta%22:%7B%22%24eq%22:%225086941171011962392%22%7D%7D"},{"rel":"describedby","href":"https://ge4e65cc87f573d-db20220526112405.adb.eu-amsterdam-1.oraclecloudapps.com/ords/admin/metadata-catalog/notas/"},{"rel":"first","href":"https://ge4e65cc87f573d-db20220526112405.adb.eu-amsterdam-1.oraclecloudapps.com/ords/admin/notas/?q=%7B%22receta%22:%7B%22%24eq%22:%225086941171011962392%22%7D%7D"}]}
And I just need to take the median and votes values ​​to create the RatingCount and RatingValue labels
Can anyone offer me an idea that solves this little problem? :-)

How to change the current time in the progress control?

Is it possible to manualy set the current time position on the timeline? I need this because I have several separated videos which represent one video and are playing in different players.
For example, the actual current time is marked as white dot, the desired current time that I want to show as red dot.
Can I do this somehow without creating completely new custom controlbar?
Middleware could be part of the solution, which allows you to intercept and modify what's sent to / received from the playback tech (video element).
var myMiddleware = function(player) {
return {
currentTime: function(ct) {
// When the video el says the currentTime is 60s, report 160s
return ct + 100;
},
setCurrentTime: function(time) {
// When the user / progress bar wants to seek to 220s, actually seek to 120s
return time - 100;
}
};
};
videojs.use('*', myMiddleware);

Here map integration in react-native mobile app

I am trying to implement heremap api in react-native project.While searching got https://www.npmjs.com/package/react-native-heremaps .But there is no proper documents to use this library. I am new to react-native. Please give me some suggestions to implement this.
! if you don't use expo please comment and I'll change the answer a bit !
First of all, as you probably know you need to make an account at HERE Developer webiste.
After you have made an account, you have to create a project(you can get a Freemium plan for free and it has plenty of requests available for free, upgrade if you need more). After that you need to "Generate App" for REST & XYZ HUB API/CLI at your project page. With that, you will recieve APP ID and APP CODE. With all this, HERE Developer Account setup is complete.
Lets jump to React Native now.
First of all you need to install a npm package called react-native-maps which we will use to display data that HERE provides. You can see installation instructions here.
After this, lets assume you have already created a component that will show the map. You need to import this:
import { Marker, Polyline } from 'react-native-maps'
import { MapView } from 'expo'
With that we have our map almost ready.
I will use axios in this example but you can use fetch to make requests to HERE if you want.
So we import axios(if you never worked with it you can learn more about it here):
import axios from 'axios'
Now, you should have coordinates of those two locations ready in a state or somewhere, and it should look something like this:
constructor(props){
super(props)
this.state = {
startingLocation: {
latitude: "xx.x",
longitude: "yy.y",
},
finishLocation: {
latitude: "xx.x",
longitude: "yy.y",
}
}
}
With "xx.x" and "yy.y" being actual coordinates you want.
So now when you have coordinates of start and finish location you can make a request to you HERE API project. It's as easy as this(I got this api from here):
// I will create a function which will call this, you can call it whenever you want
_getRoute = () => {
// we are using parseFloat() because HERE API expects a float
let from_lat = parseFloat(this.state.startingLocation.latitude)
let from_long = parseFloat(this.state.startingLocation.longitude)
let to_lat = parseFloat(this.state.finishLocation.latitude)
let to_long = parseFloat(this.state.finishLocation.longitude)
// we will save all Polyline coordinates in this array
let route_coordinates = []
axios.get(`https://route.api.here.com/routing/7.2/calculateroute.json?app_id=PUT_YOUR_APP_ID_HERE&app_code=PUT_YOUR_APP_CODE_HERE&waypoint0=geo!${from_lat},${from_long}&waypoint1=geo!${to_lat},${to_long}&mode=fastest;bicycle;traffic:disabled&legAttributes=shape`).then(res => {
// here we are getting all route coordinates from API response
res.data.response.route[0].leg[0].shape.map(m => {
// here we are getting latitude and longitude in seperate variables because HERE sends it together, but we
// need it seperate for <Polyline/>
let latlong = m.split(',');
let latitude = parseFloat(latlong[0]);
let longitude = parseFloat(latlong[1]);
routeCoordinates.push({latitude: latitude, longitude: longitude});
}
this.setState({
routeForMap: routeCoordinates,
// here we can access route summary which will show us how long does it take to pass the route, distance etc.
summary: res.data.response.route[0].summary,
// NOTE just add this 'isLoading' field now, I'll explain it later
isLoading: false,
})
}).catch(err => {
console.log(err)
})
}
NOTE There are few things to note here. First is that you have to replace APP ID and APP CODE with acutal APP ID and APP CODE from your HERE project.
Second note that I added &legAttributes=shape at the end of the request URL but it is not in the documentation. I put it there so Polyline coordinates acutally have a correct shape, if you don't put it, it will just respond with coordinates of road turns and that polyline will go over buildings and stuff, it will just look bad.
OK. So now we have coordinates to make a Polyline, let's do that.
<MapView>
<Polyline coordinates={this.state.routeForMap} strokeWidth={7} strokeColor="red" geodesic={true}/>
<Marker coordinate={{latitude: this.state.startingLocation.latitude, longitude: this.state.startingLocation.longitude}} title="Starting location"/>
<Marker coordinate={{latitude: this.state.finishLocation.latitude, longitude: this.state.finishLocation.longitude}} title="Finishlocation"/>
</MapView>
Explanation:
Polyline.coordinates will map through all of the coordinates that we have provided and draw a Polyline. strokeWidth is just how thick you want your line to be, and strokeColor is obviously color of a line.
Now, you should add a region to your MapView component to let it know what is the initial region you want to show on the map. So I suggest you to do something like this:
In state, define a region field and make it the same coordinates as starting location, and then set delta to make a bit larger view.
// so in state just add this
region: {
latitude: parseFloat("xx.x"),
longitude: parseFloat("yy.y"),
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}
And now, add region={this.state.region} to MapView.
You would be done now, but let's make sure this works every time. You need to make sure that HERE API request is complete before the map renders. I would do it like this:
// in your state define field to control if loading is finished
isLoading: true,
Now, you would call the function _getRoute() we made before in componendDidMount() lifecycle function provided by React Native. Like this:
componentDidMount() {
// when this function is finished, we will set isLoading state to false to let program know that API request has finished and now we can render the map
this._getRoute()
}
So finally a final step is to control isLoading in your render() function:
render() {
if(this.state.isLoading) {
return (
<Text>Loading...(you could also use <ActivityIndicator/> or what ever you want to show while loading the request)</Text>
)
} else {
// just put everything we already did here + stuff you already have
}
}
So here it is. I tried to make it as detailed as possible to make it easy for you and other people that will need help with this.
Don't ever hesitate to ask me anything if something is unclear or it's not working or you need more help! I'm always happy to help :D

Strava API - accurate longitude and latitude

Does anyone know a way to get the exact longitude and latitude for an activity from the strava api using a get request?
I'm trying to integrate the strava api with google maps and I'm trying to build an array with the appropriate long/lat locations, but the https://www.strava.com/api/v3/athlete/activities?per_page=100... request is only returning longitude and latitudes rounded off like: start_longitude: 2.6.
I've found a "hacky" way of retrieving the start coordinates by looping through results and then sending off another request within the loop, although this is sending WAY too many requests. - below is a snippet of my request:
// start request
$.get( "https://www.strava.com/api/v3/athlete/activities?per_page=100&access_token=ACCESSTOKEN", function (results) {
// loop through request
for (i = 0; i < results.length; i++) {
if(results[i].type === 'Run') {
// add an element to the array for each result
stravaComplete.push(true);
$.get( "https://www.strava.com/api/v3/activities/"+ results[i].id +"?per_page=5&access_token=ACCESSTOKEN", function (runs) {
if(typeof runs.segment_efforts[0] != "undefined"){
var runLatitude = runs.segment_efforts[0].segment.start_latitude;
var runLongitude = runs.segment_efforts[0].segment.start_longitude;
stravaActivityList.push({"lat": runLatitude, "lng": runLongitude});
}
// remove the element from the array
stravaComplete.pop();
// if all the elements have been removed from the array it means the request is complete
if(stravaComplete.length == 0) {
// reinitialize map
initMap(stravaActivityList);
}
});
}
}
});
Any guidance would be great. Thanks.
It's not clear if you need coordinates of start points only, or for the whole activity and what accuracy is required.
Response to query https://www.strava.com/api/v3/athlete/activities includes a field map => summary_polyline from which you should be able to extract coordinates of the whole (although simplified) activity. You can also use this polyline to display it in google maps.
If you, however, need even better accuracy you need to retrieve every activity and use map => summary_polyline or map => polyline fields.
To get full data streams should be used
Use summary_polyline[0] and summary_polyline[-1] (Ruby) instead of rounded values. See this code from Slava for an example.

Partial Address Search / Typeahead

We have an address field we want to provide typeahead for. It sits behind a login, although if we needed to we could get crafty and make that one page public for licensing compliance.
The Google Maps API is getting locked down. We used to use the "reverse geocode" portion of it to perform partial address search / typeahead for addresses - so for example if the user typed:
1600 Penn
I could hit the service and get back several suggestions, like:
1600 Pennsylvania Avenue, Washington, DC
There are several other partial address searches out there I've come across but they each have problems.
Google: $10000/yr minimum for just 7500 requests/day - ridiculous
Yahoo: Shutdown this year
Bing: Requires the page to be public / not behind login. This isn't a hard stop for us, but it would be a challenging redesign of how the page works.
Mapquest OpenStreetMap API: Searches for the exact string rather than a leading string - so it returns Penn Station instead of 1600 Pennsylvania Ave.
Mapquest OpenStreetMap data: We could download all of this and implement our own, but the CPU and data requirements would likely be too much to bite off for the time being.
We're fine with paying for usage, we'd just seek a solution closer to Amazon's $0.01/10000 requests than Google's.
UPDATE: My original answer, intact below, was given before SmartyStreets had an address autocomplete offering, which is free with a LiveAddress API subscription.
It's simple, really. The USPS has approved certain vendors for address verification/standardization services. I work for one such company, SmartyStreets, and in my experience, what you are trying to do is probably easier than you think with a good API that has a REST endpoint. Look into the LiveAddress API.
Submit a street address and at least a city/state, or zip, or both, and you'll get a list of suggested results.
NOTICE, however, that non-CASS providers such as Google Maps do address approximation, not address validation. Google - and others - make a "best guess" which is what Google and them are expert at. If you want actual existing addresses, make sure you find a service that does just that. I'll add that LiveAddress is free now and offers better performance than, for example, the USPS API.
How comes that doing it yourself isn't an option? IMO a partial search or a typeahead isn't so hard to do with a ternary trie on the address, street, city etc.pp field.
Google has released Google Places Autocomplete which resolves exactly this problem.
At the bottom of a page throw this in there:
<script defer async src="//maps.googleapis.com/maps/api/js?libraries=places&key=(key)&sensor=false&callback=googPlacesInit"></script>
Where (key) is your API key.
We've set our code up so you mark some fields to handle typeahead and get filled in by that typeahead, like:
<input name=Address placeholder=Address />
<input name=Zip placeholder=Zip />
etc
Then you initialize it (before the Google Places API has loaded typically, since that's going to land async) with:
GoogleAddress.init('#billing input', '#shipping input');
Or whatever. In this case it's tying the address typeahead to whatever input has name=Address in the #billing tag and #shipping tag, and it will fill in the related fields inside those tags for City, State, Zip etc when an address is chosen.
Setup the class:
var GoogleAddress = {
AddressFields: [],
//ZipFields: [], // Not in use and the support code is commented out, for now
OnSelect: [],
/**
* #param {String} field Pass as many arguments as you like, each a selector to a set of inputs that should use Google
* Address Typeahead via the Google Places API.
*
* Mark the inputs with name=Address, name=City, name=State, name=Zip, name=Country
* All fields are optional; you can for example leave Country out and everything else will still work.
*
* The Address field will be used as the typeahead field. When an address is picked, the 5 fields will be filled in.
*/
init: function (field) {
var args = $.makeArray(arguments);
GoogleAddress.AddressFields = $.map(args, function (selector) {
return $(selector);
});
}
};
The script snippet above is going to async call into a function named googPlacesInit, so everything else is wrapped in a function by that name:
function googPlacesInit() {
var fields = GoogleAddress.AddressFields;
if (
// If Google Places fails to load, we need to skip running these or the whole script file will fail
typeof (google) == 'undefined' ||
// If there's no input there's no typeahead so don't bother initializing
fields.length == 0 || fields[0].length == 0
)
return;
Setup the autocomplete event, and deal with the fact that we always use multiple address fields, but Google wants to dump the entire address into a single input. There's surely a way to prevent this properly, but I'm yet to find it.
$.each(fields, function (i, inputs) {
var jqInput = inputs.filter('[name=Address]');
var addressLookup = new google.maps.places.Autocomplete(jqInput[0], {
types: ['address']
});
google.maps.event.addListener(addressLookup, 'place_changed', function () {
var place = addressLookup.getPlace();
// Sometimes getPlace() freaks out and fails - if so do nothing but blank out everything after comma here.
if (!place || !place.address_components) {
setTimeout(function () {
jqInput.val(/^([^,]+),/.exec(jqInput.val())[1]);
}, 1);
return;
}
var a = parsePlacesResult(place);
// HACK! Not sure how to tell Google Places not to set the typeahead field's value, so, we just wait it out
// then overwrite it
setTimeout(function () {
jqInput.val(a.address);
}, 1);
// For the rest, assign by lookup
inputs.each(function (i, input) {
var val = getAddressPart(input, a);
if (val)
input.value = val;
});
onGoogPlacesSelected();
});
// Deal with Places API blur replacing value we set with theirs
var removeGoogBlur = function () {
var googBlur = jqInput.data('googBlur');
if (googBlur) {
jqInput.off('blur', googBlur).removeData('googBlur');
}
};
removeGoogBlur();
var googBlur = jqInput.blur(function () {
removeGoogBlur();
var val = this.value;
var _this = this;
setTimeout(function () {
_this.value = val;
}, 1);
});
jqInput.data('googBlur', googBlur);
});
// Global goog address selected event handling
function onGoogPlacesSelected() {
$.each(GoogleAddress.OnSelect, function (i, fn) {
fn();
});
}
Parsing a result into the canonical street1, street2, city, state/province, zip/postal code is not trivial. Google differentiates these localities with varying tags depending on where in the world you are, and as a warning, if you're used to US addresses, there are places for example in Africa that meet none of your expectations of what an address looks like. You can break addresses in the world into 3 categories:
US-identical - the entire US and several countries that use a similar addressing system
Formal addresses - UK, Australia, China, basically developed countries - but the way their address parts are broken up/locally written has a fair amount of variance
No formal addresses - in undeveloped areas there are no street names let alone street numbers, sometimes not even a town/city name, and certainly no zip. In these locations what you really want is a GPS location, which is not handled by this code.
This code only attempts to deal with the first 2 cases.
function parsePlacesResult(place) {
var a = place.address_components;
var p = {};
var d = {};
for (var i = 0; i < a.length; i++) {
var ai = a[i];
switch (ai.types[0]) {
case 'street_number':
p.num = ai.long_name;
break;
case 'route':
p.rd = ai.long_name;
break;
case 'locality':
case 'sublocality_level_1':
case 'sublocality':
d.city = ai.long_name;
break;
case 'administrative_area_level_1':
d.state = ai.short_name;
break;
case 'country':
d.country = ai.short_name;
break;
case 'postal_code':
d.zip = ai.long_name;
}
}
var addr = [];
if (p.num)
addr.push(p.num);
if (p.rd)
addr.push(p.rd);
d.address = addr.join(' ');
return d;
}
/**
* #param input An Input tag, the DOM element not a jQuery object
* #paran a A Google Places Address object, with props like .city, .state, .country...
*/
var getAddressPart = function(input, a) {
switch(input.name) {
case 'City': return a.city;
case 'State': return a.state;
case 'Zip': return a.zip;
case 'Country': return a.country;
}
return null;
}
Old Answer
ArcGis/ESRI has a limited typeahead solution that's functional but returns limited results only after quite a bit of input. There's a demo here:
http://www.esri.com/services/disaster-response/wildlandfire/latest-news-map.html
For example you might type 1600 Pennsylvania Ave hoping to get the white house by the time you type "1600 Penn", but have to get as far as "1600 pennsylvania ave, washington dc" before it responds with that address. Still, it could have a small benefit to users in time savings.