If I know the degress, minutes, and seconds of a location, how do I convert them to a valid location for ExifInterface.TAG_GPS_LATITUDE and ExifInterface.TAG_GPS_LONGITUDE?
I found following: https://developer.android.com/reference/android/media/ExifInterface.html#TAG_GPS_LATITUDE
But I'm not sure if I understand the format correctly. There is written following:
String. Format is "num1/denom1,num2/denom2,num3/denom3".
I'm not sure which fractions to use for each values... Always 1? Like in following code example:
String exifLatitude1 = degress+ "/1," + minutes + "/1," + seconds + "/1";
I often see strings with /1000 for the seconds, so I'm not sure if following is correct instead of the example above:
String exifLatitude2 = degress+ "/1," + minutes + "/1," + seconds + "/1000";
Can anyone tell me, which one is correct?
My working solution uses milliseconds/1000
-79.948862 becomes
-79 degrees, 56 minutes, 55903 millisecs (equals 55.903 seconds)
79/1,56/1,55903/1000
I have never checked if 79/1,56/1,56/1 would be ok, too.
I am using this code:
from https://github.com/k3b/APhotoManager/blob/FDroid/app/src/main/java/de/k3b/android/util/ExifGps.java
public static boolean saveLatLon(File filePath, double latitude, double longitude) {
exif = new ExifInterface(filePath.getAbsolutePath());
debugExif(sb, "old", exif, filePath);
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, convert(latitude));
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, latitudeRef(latitude));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, convert(longitude));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, longitudeRef(longitude));
exif.saveAttributes();
}
/**
* convert latitude into DMS (degree minute second) format. For instance<br/>
* -79.948862 becomes<br/>
* 79/1,56/1,55903/1000<br/>
* It works for latitude and longitude<br/>
* #param latitude could be longitude.
* #return
*/
private static final String convert(double latitude) {
latitude=Math.abs(latitude);
int degree = (int) latitude;
latitude *= 60;
latitude -= (degree * 60.0d);
int minute = (int) latitude;
latitude *= 60;
latitude -= (minute * 60.0d);
int second = (int) (latitude*1000.0d);
StringBuilder sb = new StringBuilder(20);
sb.append(degree);
sb.append("/1,");
sb.append(minute);
sb.append("/1,");
sb.append(second);
sb.append("/1000");
return sb.toString();
}
private static StringBuilder createDebugStringBuilder(File filePath) {
return new StringBuilder("Set Exif to file='").append(filePath.getAbsolutePath()).append("'\n\t");
}
private static String latitudeRef(double latitude) {
return latitude<0.0d?"S":"N";
}
Related
DIRECTION_URL_API = "https://maps.googleapis.com/maps/api/directions/json?"
DIRECTION_URL_API + "origin=" + origin + "&destination=" + destination + "&sensor=true" + "&mode=" +typeOpt+"&key=" + GOOGLE_API_KEY ;
I am using this format but its not working
Please suggest me :)
You can find distance following way
http://maps.googleapis.com/maps/api/directions/json?origin=21.1702,72.8311&destination=21.7051,72.9959&sensor=false&units=metric&mode=driving
origin=lat1,long1
destination=lat2,long2
Please use the below method to calculate the distance between two points
/**
* Returns Distance in kilometers (km)
*/
public static String distance(double startLat, double startLong, double endLat, double endLong) {
Location startPoint = new Location("locationA");
startPoint.setLatitude(startLat);
startPoint.setLongitude(startLong);
Location endPoint = new Location("locationA");
endPoint.setLatitude(endLat);
endPoint.setLongitude(endLong);
return String.format("%.2f", startPoint.distanceTo(endPoint) / 1000); //KMs
}
Method usage -
String mDistance = distance(startLat,
startLong,
endLat,endLng)).concat("km");
I'm writing gallery.But I got double when i use exifInterface.getAttribute(ExifInterface.TAG_EXPOSURE_TIME), it should be rational(fraction). If I open system gallery, it is rational.Please help me.Thanks.
To get precise/correct values use the new ExifInterface support library instead of the old ExifInterface.
You must add to your gradle:
compile "com.android.support:exifinterface:25.1.0"
And then ensure you use the new android.support.media.ExifInterface library instead of the old android.media.ExifInterface.
import android.support.media.ExifInterface;
String getExposureTime(final ExifInterface exif)
{
String exposureTime = exif.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
if (exposureTime != null)
{
exposureTime = formatExposureTime(Double.valudeOf(exposureTime));
}
return exposureTime;
}
public static String formatExposureTime(final double value)
{
String output;
if (value < 1.0f)
{
output = String.format(Locale.getDefault(), "%d/%d", 1, (int)(0.5f + 1 / value));
}
else
{
final int integer = (int)value;
final double time = value - integer;
output = String.format(Locale.getDefault(), "%d''", integer);
if (time > 0.0001f)
{
output += String.format(Locale.getDefault(), " %d/%d", 1, (int)(0.5f + 1 / time));
}
}
return output;
}
I have a table in wich items are stored using the geographic location based on latitude and longitude. At one point I want to retrieve only those items from the database that are in a specific range (distance or radius) of my current position.
Item (latitude, longitude)
So I followed this really nice post and tried to implement the answer with the highest votes. First thing: I had to add some more columns when storing the items. This enables the select-statement later on:
Item (lat, lat_cos, lat_sin, lng, lng_cos, lng_sin)
So far so good, but when I then try to build the query, I go nuts. If I use the less-symbol < then all items are retrieved (even if too far away). But if I use the greater-symbol > no items are retrieved. But I know that there are items in my range.
Question: So what am I doing wrong with that term?
WHERE (coslat * LAT_COS * (LNG_COS * coslng + LNG_SIN * sinlng)) + sinlat * LAT_SIN > cos_allowed_distance
Here's the code that I'm using for querying my ContentProvider. The ContentInfo class has fields for sortOrder, selection and selectionArgs:
public static ContentInfo buildDistanceWhereClause(double latitude, double longitude, double distanceInKilometers) {
final double coslat = Math.cos(Math.toRadians(latitude));
final double sinlat = Math.sin(Math.toRadians(latitude));
final double coslng = Math.cos(Math.toRadians(longitude));
final double sinlng = Math.sin(Math.toRadians(longitude));
// (coslat * LAT_COS * (LNG_COS * coslng + LNG_SIN * sinlng)) + sinlat * LAT_SIN > cos_allowed_distance
final String where = "(? * ? * (? * ? + ? * ?)) + ? * ? > ?";
final String[] whereClause = new String[] {
String.valueOf(coslat),
ItemTable.COLUMN_LATITUDE_COS,
ItemTable.COLUMN_LONGITUDE_COS,
String.valueOf(coslng),
ItemTable.COLUMN_LONGITUDE_SIN,
String.valueOf(sinlng),
String.valueOf(sinlat),
ItemTable.COLUMN_LATITUDE_SIN,
String.valueOf(Math.cos(distanceInKilometers / 6371.0))
};
return new ContentInfo(null, where, whereClause);
}
}
I don't actually see the problem, but I re-engineered the selection/where so it's easier to read. And now it works. You can also checkout this fiddle.
public static String buildDistanceWhereClause(double latitude, double longitude, double distanceInKilometers) {
// see: http://stackoverflow.com/questions/3126830/query-to-get-records-based-on-radius-in-sqlite
final double coslat = Math.cos(Math.toRadians(latitude));
final double sinlat = Math.sin(Math.toRadians(latitude));
final double coslng = Math.cos(Math.toRadians(longitude));
final double sinlng = Math.sin(Math.toRadians(longitude));
final String format = "(%1$s * %2$s * (%3$s * %4$s + %5$s * %6$s) + %7$s * %8$s) > %9$s";
final String selection = String.format(format,
coslat, COLUMN_LATITUDE_COS,
coslng, COLUMN_LONGITUDE_COS,
sinlng, COLUMN_LONGITUDE_SIN,
sinlat, COLUMN_LATITUDE_SIN,
Math.cos(distanceInKilometers / 6371.0)
);
Log.d(TAG, selection);
return selection;
}
I trying to match hardcoded latitude an longitude with dynamic latitude and longitude, but its not showing correct output, can anyone help me to sort out this error
My code is
String Log = "-122.084095";
String Lat = "37.422005";
try {
if ((Lat.equals(latitude)) && (Log.equals(longitude))) {
AudioManager audiM = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
audiM.setRingerMode(AudioManager.RINGER_MODE_SILENT);
Toast.makeText(getApplicationContext(),
"You are at home",
Toast.LENGTH_LONG).show();
} else {
AudioManager auMa = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
auMa.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
Toast.makeText(getApplicationContext(),
"You are at office ", Toast.LENGTH_LONG)
.show();
}
} catch (Exception e) {
e.printStackTrace();
}
it always goes for else part...
You don't want to use a String comparison here as you can't guarantee the level of accuracy with the real-time location.
The best way to handle this would be to determine the distance between the points and then determine if it's close enough for you to consider, approx, the same.
For this, we use distanceBetween or distanceTo
Docs are here and here
Examples can be found here. Here's one of those examples:
Location locationA = new Location("point A");
locationA.setLatitude(pointA.getLatitudeE6() / 1E6);
locationA.setLongitude(pointA.getLongitudeE6() / 1E6);
Location locationB = new Location("point B");
locationB.setLatitude(pointB.getLatitudeE6() / 1E6);
locationB.setLongitude(pointB.getLongitudeE6() / 1E6);
double distance = locationA.distanceTo(locationB);
The latitude and longitude are variables which vary from point to point, matter of fact they keep on changing while standing on the same spot, because it is not precise.
Instead of comparing the Strings, take a rounded value of the lat and long (in long or float ) and check those values within a certain range. That will help you out with the "Home" and "Office " thing.
For e.g :
String Log = "22.084095";
String Lat = "37.422005";
double lng=Double.parseDouble(Log);
double lat=Double.parseDouble(Lat);
double upprLogHome=22.1;
double lwrLogHome=21.9;
double upprLatHome=37.5;
double lwrLatHome=37.3;
// double upprLogOfc=;
// double lwrLogOfc=;
// double upprLatOfc=;
// double lwrLatOfc=;
if(lng<upprLogHome && lng>lwrLogHome && lat<upprLatHome &&lat>lwrLatHome )
{
System.out.println("You are Home");
}
/* else if(lng<upprLogOfc && lng>lwrLogOfc && lat<upprLatOfc &&lat>lwrLatOfc )
{
System.out.println("You are Home");
}*/
else
System.out.println("You are neither Home nor Ofc");
But for the negative lat long you have to reverse the process of checking.
your matching is okay but you probably should not check for a gps location like this.
You should convert the location to something where you can check that you are in 10m radius of the location.
A nicer way would be to leave the long/lat as doubles and compare the numbers.
if(lat > HOME_LAT - 0.1 && lat < HOME_LAT + 0.1 && ...same for lon... ){}
Try this,
Use google map api to pass lat and long value you will get formatted address. And also pass dynamic lat and lng value same google api you will get formatted address. And then match two formatted address you will get result. i suggest this way you can try this
Use this google api. http://maps.googleapis.com/maps/api/geocode/json?latlng=11.029494,76.954422&sensor=true
Reena, its very easy, Check out below code. You need to use "equalsIgnoreCase()" instead of
"equals".
if ((Lat.equalsIgnoreCase(latitude)) && (Log.equalsIgnoreCase(longitude))) {
should work
Example below :
// Demonstrate equals() and equalsIgnoreCase().
class equalsDemo {
public static void main(String args[]) {
String s1 = "Hello";
String s2 = "Hello";
String s3 = "Good-bye";
String s4 = "HELLO";
System.out.println(s1 + " equals " + s2 + " -> " +
s1.equals(s2));
System.out.println(s1 + " equals " + s3 + " -> " +
s1.equals(s3));
System.out.println(s1 + " equals " + s4 + " -> " +
s1.equals(s4));
System.out.println(s1 + " equalsIgnoreCase " + s4 + " -> " +
s1.equalsIgnoreCase(s4));
}
}
You can print dynamice Latitute and Longitute to Logcat and check with hardcoded Latitute and Longitute
I am writing GPS coordinates to my JPEG image, and the coordinates are correct (as demonstrated by my logcat output) but it appears that it's being corrupted somehow. Reading the exif data results in either null values or, in the case of my GPS: 512.976698 degrees, 512.976698 degrees. Can anyone shed some light on this problem?
writing it:
try {
ExifInterface exif = new ExifInterface(filename);
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, latitude);
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, longitude);
exif.saveAttributes();
Log.e("LATITUDE: ", latitude);
Log.e("LONGITUDE: ", longitude);
} catch (IOException e) {
e.printStackTrace();
}
and reading it:
try {
ExifInterface exif = new ExifInterface("/sdcard/globetrotter/mytags/"+ TAGS[position]);
Log.e("LATITUDE EXTRACTED", exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE));
Log.e("LONGITUDE EXTRACTED", exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE));
} catch (IOException e) {
e.printStackTrace();
}
It goes in (for example) 37.715183, -117.260489 and comes out 33619970/65540, 14811136/3368550, 33619970/65540, 14811136/3368550. Am I doing it wrong?
EDIT:
So, the problem is I am not encoding it in the properly defined format, which is something like you see here:
Can anyone explain what this format is? Obviously the first number is 22/1 = 22 degrees, but I can't figure out how to compute the decimal there.
GPSLatitude
Indicates the latitude. The latitude is expressed as three
RATIONAL values giving the degrees,
minutes, and seconds, respectively.
If latitude is expressed as degrees,
minutes and seconds, a typical format
would be dd/1,mm/1,ss/1. When degrees
and minutes are used and, for
example, fractions of minutes are
given up to two decimal places, the
format would be dd/1,mmmm/100,0/1.
https://docs.google.com/viewer?url=http%3A%2F%2Fwww.exif.org%2FExif2-2.PDF
The Android docs specify this without explanation: http://developer.android.com/reference/android/media/ExifInterface.html#TAG_GPS_LATITUDE
Exif data is standardized, and GPS data must be encoded using geographical coordinates (minutes, seconds, etc) described above instead of a fraction. Unless it's encoded in that format in the exif tag, it won't stick.
How to encode: http://en.wikipedia.org/wiki/Geographic_coordinate_conversion
How to decode: http://android-er.blogspot.com/2010/01/convert-exif-gps-info-to-degree-format.html
Here is some code I've done to geotag my pictures. It's not heavily tested yet, but it seems to be ok (JOSM editor and exiftool read location).
ExifInterface exif = new ExifInterface(filePath.getAbsolutePath());
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, GPS.convert(latitude));
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, GPS.latitudeRef(latitude));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, GPS.convert(longitude));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, GPS.longitudeRef(longitude));
exif.saveAttributes();
And class GPS is here. (method could be shorter, but it's readable at least)
/*
* #author fabien
*/
public class GPS {
private static StringBuilder sb = new StringBuilder(20);
/**
* returns ref for latitude which is S or N.
* #param latitude
* #return S or N
*/
public static String latitudeRef(double latitude) {
return latitude<0.0d?"S":"N";
}
/**
* returns ref for latitude which is S or N.
* #param latitude
* #return S or N
*/
public static String longitudeRef(double longitude) {
return longitude<0.0d?"W":"E";
}
/**
* convert latitude into DMS (degree minute second) format. For instance<br/>
* -79.948862 becomes<br/>
* 79/1,56/1,55903/1000<br/>
* It works for latitude and longitude<br/>
* #param latitude could be longitude.
* #return
*/
synchronized public static final String convert(double latitude) {
latitude=Math.abs(latitude);
int degree = (int) latitude;
latitude *= 60;
latitude -= (degree * 60.0d);
int minute = (int) latitude;
latitude *= 60;
latitude -= (minute * 60.0d);
int second = (int) (latitude*1000.0d);
sb.setLength(0);
sb.append(degree);
sb.append("/1,");
sb.append(minute);
sb.append("/1,");
sb.append(second);
sb.append("/1000");
return sb.toString();
}
}
Other answers delivered nice background info and even an example. This is not a direct answer to the question but I would like to add an even simpler example without the need to do any math. The Location class delivers a nice convert function:
public String getLonGeoCoordinates(Location location) {
if (location == null) return "0/1,0/1,0/1000";
// You can adapt this to latitude very easily by passing location.getLatitude()
String[] degMinSec = Location.convert(location.getLongitude(), Location.FORMAT_SECONDS).split(":");
return degMinSec[0] + "/1," + degMinSec[1] + "/1," + degMinSec[2] + "/1000";
}
I stored the return value in my image and the tag is parsed fine. You can check your image and the geocoordinates inside here: http://regex.info/exif.cgi
Edit
#ratanas comment translated to code:
public boolean storeGeoCoordsToImage(File imagePath, Location location) {
// Avoid NullPointer
if (imagePath == null || location == null) return false;
// If we use Location.convert(), we do not have to worry about absolute values.
try {
// c&p and adapted from #Fabyen (sorry for being lazy)
ExifInterface exif = new ExifInterface(imagePath.getAbsolutePath());
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, getLatGeoCoordinates(location));
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, location.getLatitude() < 0 ? "S" : "N");
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, getLonGeoCoordinates(location));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, location.getLongitude() < 0 ? "W" : "E");
exif.saveAttributes();
} catch (IOException e) {
// do something
return false;
}
// Data was likely written. For sure no NullPointer.
return true;
}
Here are some nice LatLong converter: latlong.net
ExifInterface exif = new ExifInterface(compressedImage.getPath());
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE,gpsTracker.dec2DMS(gpsTracker.getLatitude()));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE,gpsTracker.dec2DMS(gpsTracker.getLongitude()));
Convertor double to String
String dec2DMS(double coord) {
coord = coord > 0 ? coord : -coord;
String sOut = Integer.toString((int)coord) + "/1,";
coord = (coord % 1) * 60;
sOut = sOut + Integer.toString((int)coord) + "/1,";
coord = (coord % 1) * 60000;
sOut = sOut + Integer.toString((int)coord) + "/1000";
return sOut;
}
The most modern and shortest solution (with AndroidX) is using ExifInterface.setGpsInfo(Location), for example:
ExifInterface exif = new ExifInterface(filename);
Location location = new Location(""); //may be empty
location.setLatitude(latitude); //double value
location.setLongitude(longitude); //double value
exif.setGpsInfo(location)
exif.saveAttributes();
Sources: one and two
check android source code: https://android.googlesource.com/platform/frameworks/base/+/android-4.4.2_r2/core/java/android/hardware/Camera.java
/**
* Sets GPS longitude coordinate. This will be stored in JPEG EXIF
* header.
*
* #param longitude GPS longitude coordinate.
*/
public void setGpsLongitude(double longitude) {
set(KEY_GPS_LONGITUDE, Double.toString(longitude));
}
So it's a direct print, my log supports it as well: ExifInterface.TAG_GPS_LONGITUDE : -121.0553966
My conclusion is setting it as direct print is fine.