Simple question need simple answer.
How can I track the residence time in a specific geofence.
When I add geofence with Trigger on Enter, will I automatic receive a trigger when I leave that geofence. Basically I need to remember the time when I enter a geofence and when I leave that geofence so I can subtract leave-enter time and have my duration time. But I believe its not that easy to do that. So any other idea or advise how to solve that problem efficient ?
Thanks
I found one solution for this problem. In order to determine the dwell time in a geofence i have to get the time when user trigger a geofence and then i use to calculate if user leave the geofence with this mathematical method
Handler handler = new Handler();
getCurrentLatLng();
final Runnable myRunnable = new Runnable() {
public void run() {
GeofenceFinished myFinishedGeofence = db.getGeofenceFinishedByID("Finished" + id);
if (!isUserInRegion(latitude, longitude, myLatLng.latitude, myLatLng.longitude, rd)
|| myFinishedGeofence == null) {
String time = convertTime(endTime - startTime);
// get the finishedGeofence and set the duration of stay time
if (myFinishedGeofence != null)
setDurationOfStay("Finished" + id, time, getCurrentTime(System.currentTimeMillis()));
Toast.makeText(mContext, "Finish to determine duration of stay of " + myFinishedGeofence.getAddress(), Toast.LENGTH_SHORT).show();
handler.removeCallbacks(this);
} else {
getCurrentLatLng();
endTime += Constants.DELAY;
handler.postDelayed(this, Constants.DELAY);
}
}
private void setDurationOfStay(String geofenceid, String time, String endTime) {
if (db == null) db = GeofenceDatabaseHelper.getInstance(mContext);
if (!db.setDurationOfFinishedGeofence(geofenceid, time, endTime)) {
Log.i("setDurationOfStay", "fail");
}
}
};
handler.postDelayed(myRunnable, Constants.DELAY);
private boolean isUserInRegion(double firstLat, double firstLog, double curLat, double curLog, float radius) {
double distance = calculateDistance(firstLat, firstLog, curLat, curLog);
return distance <= radius;
}
To calculate the distance of user and center of geofence, i need to convert the current latitude and longitude in meter, so i can calculate that with the parametic equation for circle
So I use this approach from github
// https://github.com/mgavaghan/geodesy
private double calculateDistance(double firstLat, double firstLog, double curLat, double curLog) {
GeodeticCalculator geoCalc = new GeodeticCalculator();
Ellipsoid reference = Ellipsoid.WGS84;
GlobalPosition pointA = new GlobalPosition(firstLat, firstLog, 0.0); // Point A
GlobalPosition userPos = new GlobalPosition(curLat, curLog, 0.0); // Point B
// Distance between Point A and Point B
double distance = geoCalc.calculateGeodeticCurve(reference, userPos, pointA).getEllipsoidalDistance();
return distance;
}
hope it helps someone :D
Related
I am currently looking into (existing) solution for iOS under Xamarin. I have a map with following code:
public override async Task<string> ResolveLatLngToAddress(double lat, double lng, MapAddressFormat addressFormat)
{
var geocoder = new CLGeocoder();
try
{
var placemarks = await geocoder.ReverseGeocodeLocationAsync(new CLLocation(lat, lng));
if (placemarks.Length > 0)
{
var placemark = placemarks[0];
switch (addressFormat)
{
case MapAddressFormat.AddressFormatFull:
{
return FormatUtils.Join(true, placemark.Name, placemark.Locality, placemark.SubLocality);
}
case MapAddressFormat.AddressFormatNoNumber:
{
return FormatUtils.Join(true, placemark.Thoroughfare, placemark.Locality, placemark.SubLocality);
}
}
}
}
catch (Foundation.NSErrorException e)
{
// Unable to find a location with the supplied latitude and longitude
}
return null;
}
}
The code works really well when user moves the pin around, there is a textbox control that displays currently selected address. Once the user starts zooming in and out however, the application breaks and the function stops working.
I have done some research and I understand that CLGeocoder class works in a way that if the users starts too many requests, the response slows down and then stops completely (https://developer.apple.com/documentation/corelocation/clgeocoder).
I can see that the problem is that the event is triggered multiple times during the zoom, e.g. zooming in triggers for example 20 requests for location resolution.
I would to trigger the location resolution only after the user finished zooming, is it possible to achieve somehow, for example with delayed binding?
Please note that I am new to both iOS development and Xamarin.
Thank you very much for any help.
thank you for your comments. I have eventually solved it by caching previously queried location and re-querying only if the lat/lng coordinates changed by certain margin which I estimated to be 1 meter (perfectly fine for my purposes).
private double _cached_lat = 999;
private double _cached_lng = 9999;
private string _cached_location = "";
public override async Task<string> ResolveLatLngToAddress(double lat, double lng, MapAddressFormat addressFormat)
{
double latDif = System.Math.Abs(_cached_lat - lat);
double lngDif = System.Math.Abs(_cached_lng - lng);
//precission - around 1m
double geo_precission = 0.000005;
if (((System.Math.Abs (_cached_lat - lat) < geo_precission) && (System.Math.Abs (_cached_lng - lng) < geo_precission)) && (!string.IsNullOrEmpty(_cached_location)))
{
return _cached_location;
}
var geocoder = new CLGeocoder();
In this the error being displayed is mentioned below. I have searched online for the right answer none is applicable so far. I am trying to toast a simple message upon detecting a sudden change in the accelerometer readings so as to detect a fall. I don't think there is any other mistake in the code, if there is you are most welcome to rectify it.
Error: cannot find symbol method maketext(MainActivity,String,int)
This is my Code:
#Override
public void onSensorChanged(SensorEvent event) {
if (started) {
double x = event.values[0];
double y = event.values[1];
double z = event.values[2];
long timestamp = System.currentTimeMillis();
Data data = new Data(timestamp, x, y, z);
sensorData.add(data);
}
if(event.sensor.getType()==Sensor.TYPE_ACCELEROMETER){
double gacc=SensorManager.STANDARD_GRAVITY;
double a=event.values[0];
double b=event.values[1];
double c=event.values[2];
long mintime=System.currentTimeMillis();
boolean min = false;
boolean max = false;
int m = 0;
double xyz=Math.round(Math.sqrt(Math.pow(a,2)+Math.pow(b,2)+Math.pow(c,2)));
if(xyz<=3.0){
min = true;
}
if(min==true){
m++;
if(xyz>=14){
max=true;
}
}
if(min && max==true){
Toast.maketext(MainActivity.this,"FALL DETECTED!",Toast.LENGTH_LONG).show();
m=0;
min=false;
max=false;
}
if (m>4) {
m=0;
min=false;
max=false;
}
}
}
You are calling maketext instead of makeText. Note that camelCase.
Replace with:
Toast.makeText(MainActivity.this,"FALL DETECTED!",Toast.LENGTH_LONG).show();
After you correct it, make sure that you are using the android.widget.Toast.
How to get the most accurate geo-location on an android device, I tried following code snippet But on Nexus 4 and 10, it doesn't work even after the gps is turned on :
LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
String locationProvider = locationManager.getBestProvider(new Criteria(), true);
if (locationProvider != null) {
//Log.d(TAG, "locationProvider is not null");
Location lastKnownLocation = locationManager.getLastKnownLocation(locationProvider);
if (lastKnownLocation != null) {
AppConstants.lat = latLon[0] = lastKnownLocation.getLatitude();
AppConstants.lon = latLon[1] = lastKnownLocation.getLongitude();
retryGenerating = false;
//Log.d(TAG, "lastKnownLocation, lat = " + AppConstants.lat + ", lon = " + AppConstants.lon);
}
}
The most accurate location you get by explicitly setting the LocationProvider to GPS Provider. Expect usually 3-6m accuracy, up to 30m in urban canyons.
You need to to be outside and have free view to sky.
Expect a delay of 20-40 s for the first valid location after going outside.
I'm working on a mobile game and need the game clock to pause when the game loses focus, for example when call comes in. I have everything working except that when the game gains focus again the time adjusts to as if it had been running the whole time. If someone is on a 3 minute call when they get back they should find the game time where it was.
Here is my code:
public function showTime(event:Event){
gameTime = getTimer()-gameStartTime;
timeDisplay.text = "Time: "+clockTime(gameTime);
}
public function clockTime(ms:int) {
var seconds:int = Math.floor(ms/1000);
var minutes:int = Math.floor(seconds/60);
seconds -= minutes*60;
var timeString:String = minutes+":"+String(seconds+100).substr(1,2);
return timeString;
}
public function onActivate(event:Event):void {
addEventListener(Event.ENTER_FRAME, showTime);
}
public function onDeactivate(event:Event):void {
removeEventListener(Event.ENTER_FRAME, showTime);
}
I've been Googling this for two days and am stuck. Could someone please point me in the right direction? Some sample code would be a benefit too since I'm pretty new to AS3. Thanks!
Rich
You can check out this Stopwatch tutorial that I wrote a while back:
http://www.popamihai.com/2010/10/flex/using-the-timer-class-in-flex-to-display-a-simple-stopwatch/
It is written with Flex Builder 3 but you could easily copy/paste the code. Example with view source enabled is also included
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" verticalAlign="middle"
backgroundColor="white" viewSourceURL="srcview/index.html" creationComplete="init()">
<mx:Script>
<![CDATA[
import flash.utils.getTimer;
import flash.utils.Timer;
import flash.events.TimerEvent;
private const TIMER_INTERVAL:int = 50;
private var timerAtStart:Number = 0;
private var timerAtPause:Number = 0;
private var t:Timer;
private var d:Date;
[Bindable] private var sec:int = 0;
[Bindable] private var min:int = 0;
[Bindable] private var mls:int = 0;
private function init():void {
t = new Timer(TIMER_INTERVAL);
t.addEventListener( TimerEvent.TIMER, updateTimer );
}
private function updateTimer( evt:TimerEvent ):void{
d = new Date( getTimer() - timerAtStart + timerAtPause);
min = d.minutes;
sec = d.seconds;
mls = d.milliseconds;
}
private function startPauseTimer( event:MouseEvent ):void{
if ( event.target.label == "start" ) {
event.target.label = "pause";
timerAtStart = getTimer();
t.start();
} else {
event.target.label = "start";
timerAtPause = min * 60000 + sec * 1000 + mls;
t.stop();
}
}
]]>
</mx:Script>
<mx:Label text="{min + ' : ' + sec + ' : ' + mls}" fontSize="16" fontWeight="bold"/>
<mx:HBox>
<mx:Button label="start" click="startPauseTimer( event )"/>
</mx:HBox>
</mx:Application>
You're calculating your time based on the current time - gameStartTime. You either need to take the delay into account (calculate the total time stopped by using getTimer() in the onActivate() and onDeactivate() functions, then add that difference to gameStartTime), or if you don't want to change gameStartTime, then you calcuate your time based on delta time. Something like:
private var m_prevTime:int = 0;
public function startGame():void
{
// set the prevTime only when you start the game, otherwise you'll
// take into account all the time for flash to get going, your init,
// opening screens etc
this.m_prevTime = getTimer();
}
public function showTime(event:Event)
{
// get the difference in time
var currTime:int = getTimer();
gameTime += currTime - this.m_prevTime // currTime - prevTime = delta time
this.m_prevTime = currTime; // hold the current time
// display it
timeDisplay.text = "Time: "+clockTime(gameTime);
}
public function onActivate(event:Event):void
{
// we're reactivating, so make sure prev time is updated
this.m_prevTime = getTimer();
addEventListener(Event.ENTER_FRAME, showTime);
}
You'll miss out on a frame's time when coming back from the update, but it's not noticable (or you can subtract a frame's time in the onActivate() function)
Instead of using getTimer(), you should create a timer object and add an event listener to "tick" to update the clock. You can even set it to tick once per second so you don't have to convert the MS.
public function onActivate(event:Event):void {
myTimer.start();
}
public function onDeactivate(event:Event):void {
myTimer.stop();
}
See the timer reference: http://www.adobe.com/livedocs/flash/9.0/ActionScriptLangRefV3/flash/utils/Timer.html
I am working on gps based application. I am using LocationProvider and calling setlocationListener through it code is like
LocationProvider lp = LocationProvider.getInstance(null);
if (lp != null) {
lp.setLocationListener(new LocationListenerImpl(), 2, 1, 1);
} else {
Dialog.alert("GPS NOT SUPPORTED!");
retval = false;
}
} catch (LocationException e) {
System.out.println("GPS Error: " + e.toString());
}
return retval;
}
private class LocationListenerImpl implements LocationListener {
public void locationUpdated(LocationProvider provider, Location location) {
if (location.isValid()) {
heading = location.getCourse();
longitude = location.getQualifiedCoordinates().getLongitude();
latitude = location.getQualifiedCoordinates().getLatitude();
altitude = location.getQualifiedCoordinates().getAltitude();
speed = location.getSpeed();
System.out.println("Current latitude:"+latitude);
System.out.println("Current longitude:"+longitude);
System.out.println("Current speed:"+speed);
// This is to get the Number of Satellites
String NMEA_MIME = "application/X-jsr179-location-nmea";
satCountStr = location.getExtraInfo("satellites");
if (satCountStr == null) {
satCountStr = location.getExtraInfo(NMEA_MIME);
}
// this is to get the accuracy of the GPS Cords
QualifiedCoordinates qc = location.getQualifiedCoordinates();
accuracy = qc.getHorizontalAccuracy();
}
}
it doesnt give an error but dont even work out so help with the same.the control dont get transferred to LocationListenerImpl()...
I am using BlackBerry_JDE_PluginFull_1.0.0.67 with eclipse-java-galileo-SR1-win32 on Blackberry 8800 simulator..
Any assistence is grately appreciated....
Thanking you in advance.
try this:
lp.setLocationListener(new LocationListenerImpl(), 2, -1, -1);
Acc to me
u set 2,1,1 which means that time interval is 2 sec after which location updation will be automatically called
1 sec indicates time out
1 sec indicates max age
ur gps times out before going ahead for gps location update method.so try it with setting it to default value = -1
You have to enable the GPS in the simulator, is it enabled when you try?
If you're getting a NullPointerException then you should check in the OS Options that the GPS has Location activated.