How to write GPS coordinates to EXIF data on Android - geolocation

I'm trying to write a geotagging app where the user selects an image form the photo gallery, and then that image gets the current GPS coordinates written to it through its EXIF file.
So far I am able to pull up the gallery, select an image, and find my current GPS coordinates, but I cannot write those coordinates to the EXIF file. Whenever I do select an image, I can view it, but no data is written to the EXIF file.
I have looked up several examples for how to write to EXIF, and my code looks correct (to me at least). Can anyone help me figure out why I am not writing any data?
Here's my code:
//place GPS cords into exif file
try {
ExifInterface exif = new ExifInterface("/sdcard/dcim/100MEDIA/IMAG0020.jpg");
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, DMSconv(lat));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE,DMSconv(lon));
if (lat > 0)
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "N");
else
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "S");
if (lon > 0)
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "E");
else
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "W");
exif.saveAttributes();
} catch (IOException e) {
e.printStackTrace();
}
//convert from decimal degrees to DMS
String DMSconv(double coord) {
coord = (coord > 0) ? coord : (-1)*coord; // -105.9876543 -> 105.9876543
String sOut = Integer.toString((int)coord) + "/1,"; // 105/1,
coord = (coord % 1) * 60; // .987654321 * 60 = 59.259258
sOut = sOut + Integer.toString((int)coord) + "/1,"; // 105/1,59/1,
coord = (coord % 1) * 6000; // .259258 * 6000 = 1555
sOut = sOut + Integer.toString((int)coord) + "/1000"; // 105/1,59/1,15555/1000
return sOut;
}
Thanks for the help!
Edit: At this time I was trying to hardcode the file path and name into ExifInterface, so that's why it says "/sdcard/dcim/100MEDIA/IMAG0020.jpg"instead of filename. Could that have anything to do with my problem?

Solved it. Hardcoding the file path was the problem. I used the code
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String picturePath = cursor.getString(columnIndex);
to obtain picturePath which was then used in
ExifInterface exif = new ExifInterface(picturePath);

Related

How do we directly read a file from ".bin" by avoiding looping as below

I am trying to fetch a file from binary data which has about 4700 files. How do i directly point a specific file avoiding loop(while) as below.
It is taking good amount of time for me to read files which are at index 3000+
I am going through a while iteration where filename is being compared for each iteration. If File name is matching then i am trying to read the uncompressed data and saving to documents directory.
Could any one suggest me a way to deal with this.
struct FILE_DATA buffer;
FILE *myFile = fopen(binaryPath, "rb");
f_name = f_name.lowercaseString;
const char * fileNameChar = f_name.UTF8String;
if (myFile == NULL)
{
fputs("File error", stderr);
completionhandler(false,#"");
}
// Go to where the file count spec is: byte 12
fseek(myFile, 12, SEEK_SET);
// Should be a sizeof 40.
int temp = sizeof(buffer);
// Get the Table Offset.
uint32_t loc;
fread(&loc, 4, 1, myFile);
rewind(myFile);
// Go to the beginning of the file tables.
fseek(myFile, loc, SEEK_SET);
// Variable to store where the chart archive will be.
uint32_t loc2 = 0;
int _fileFound = 0;
// Copy the file into the buffer:
while(fread(&buffer,1, temp, myFile) != 0)
{
// Reads the next 40 bytes into the struct.
DebugLog("Buffer Name : %s == ",buffer.name);
// If the names are a match, then mark the location.
if(strcmp(buffer.name, fileNameChar) == 0)
{
_fileFound = 1;
loc2 = buffer.offset;
break;
}
}
if(_fileFound == 0)
{
// Alert if the file was not found.
fputs("File Not Found", stderr);
completionhandler(false,#"");
}
// Navigate to the location of the file.
fseek(myFile, loc2, SEEK_SET);
uLongf unCompSize = buffer.size_d;
uLongf compSize = buffer.size_c;
char *rawDataBuff = malloc(buffer.size_c);
//allocate buffer to hold the compressed TCL file
char * unCompDataBuff = malloc(buffer.size_d);
//allocate buffer to hold the decompressed TCL file
if (rawDataBuff == NULL){
fputs ("Memory error",stderr);
completionhandler(false,#"");
}
fread(rawDataBuff,buffer.size_c, 1,myFile);
//read in compress TCL file into the buffer
int z_result = uncompress((Bytef*)unCompDataBuff, &unCompSize, (const Bytef*)rawDataBuff, compSize);
DebugLog("status %d",z_result);

How to check if anyone is enter/left in/from particular boundary area even if application is in background mode or kill mode?

I want to check if anyone enter in allocated boundary then i have to alert that user like "You are entered" and when user leaves then "You left". I am using .KML file for draw boundary in which there are more than latitude and longitude. Here i attached screenshot for the same.So, my concern is that how can i detect that anyone is entered within this boundary and left from that boundary. Thank you in advance
Boundary looks like this.Red color line is boundary.
Use map rects. Here's an example using the map's current visible rect. With regards to your question, you could use convertRegion:toRectToView: to first convert your region to a MKMapRect beforehand.
MKMapPoint userPoint = MKMapPointForCoordinate(mapView.userLocation.location.coordinate);
MKMapRect mapRect = mapView.visibleMapRect; // find visible map rect
//MKMapRect mapRect = [self getMapRectUsingAnnotations:arrCordinate];//find custom map rect
BOOL inside = MKMapRectContainsPoint(mapRect, userPoint);
MKMapRect mapRect = mapView.visibleMapRect;
Create your custom mapRect using your boundary region from multiple latitude and longitude
- (MKMapRect) getMapRectUsingAnnotations:(NSArray*)arrCordinate {
MKMapPoint points[[arrCordinate count]];
for (int i = 0; i < [arrCordinate count]; i++) {
points[i] = MKMapPointForCoordinate([arrCordinate[i] MKCoordinateValue]);
}
MKPolygon *poly = [MKPolygon polygonWithPoints:points count:[arrCordinate count]];
return [poly boundingMapRect];
}
Geofencing will not work on complex polygon shaped regions. May be you can solve the problem with some other approach. For instance divide the region into smaller CLCircularRegion and then develop aggregate logic for the case where you have to show notification for all those locationManager:didEnterRegion: and locationManager:didExitRegion: callbacks. But keep in mind that only a max of 20 simultaneous monitored regions per app are allowed.
Refer https://forums.developer.apple.com/thread/21323 phillippk1 suggestion for other possible approach.
Try this code. This is based on Winding Number Algorithm. This works for complex shapes such as your red line.
typedef struct {
double lon;
double lat;
} LATLON;
// returns true if w/in region
bool chkInRegion(LATLON poi, int npoi, LATLON *latlon)
{
int wn = 0;
for (int i = 0 ; i < npoi-1 ; i++) {
if (latlon[i].lat <= poi.lat && latlon[i+1].lat > poi.lat) {
double vt = (poi.lat - latlon[i].lat)/(latlon[i+1].lat - latlon[i].lat);
if (poi.lon < (latlon[i].lon + (vt * (latlon[i+1].lon - latlon[i].lon)))) {
wn++;
}
} else if (latlon[i].lat > poi.lat && latlon[i+1].lat <= poi.lat) {
double vt = (poi.lat - latlon[i].lat)/(latlon[i+1].lat - latlon[i].lat);
if (poi.lon < (latlon[i].lon + (vt * (latlon[i+1].lon - latlon[i].lon)))) {
wn--;
}
}
}
return wn < 0;
}
// test data
LATLON llval[] = {
{100,100},
{200,500},
{600,500},
{700,100},
{400,300},
{100,100}
};
#define NLATLON (sizeof(llval)/sizeof(LATLON))
int main(int argc, char **argv) {
while (1) {
char buf[1024];
fprintf(stderr, "lon = ");
fgets(buf, sizeof(buf), stdin);
double lon = atof(buf);
fprintf(stderr, "lat = ");
fgets(buf, sizeof(buf), stdin);
double lat = atof(buf);
LATLON ltest;
ltest.lat = lat;
ltest.lon = lon;
if (chkInRegion(ltest, NLATLON, llval)) {
fprintf(stderr, "\n*** in region ***\n\n");
} else {
fprintf(stderr, "\n=== outside ===\n\n");
}
}
return 0;
}

How to read Arduino float values on OSX with Bluetooth LE (BLE mini module)

The SimpleControls example of the Red Bear Labs BLE Mini module (https://github.com/RedBearLab/iOS/tree/master/Examples/SimpleControls_OSX) enables to send analog readings (e.g. temperature sensor) from an Arduino to iOS / OSX with following Arduino code:
uint16_t value = analogRead(ANALOG_IN_PIN)
BLEMini_write(0x0B);
BLEMini_write(value >> 8);
BLEMini_write(value);
However, I tried to convert the raw analog readings (e.g. 162) into actual temperature reading (e.g. degree celsius / 27.15) and transmit the conversion to iOS / OSX, but on OSX I just read strange values (e.g. 13414). The Arduino code I used is following:
int reading = analogRead(ANALOG_IN_PIN);
float voltage = reading * 5.0;
float temp = (voltage - 0.5) * 100;
int tempINT = temp;
uint16_t value = tempINT;
BLEMini_write(0x0B);
BLEMini_write(value >> 8);
BLEMini_write(value);
The code-part of the OSX-app is following:
-(void) bleDidReceiveData:(unsigned char *)data length:(int)length
{
NSLog(#"Length: %d", length);
// parse data, all commands are in 3-byte
for (int i = 0; i < length; i+=3)
{
NSLog(#"0x%02X, 0x%02X, 0x%02X", data[i], data[i+1], data[i+2]);
if (data[i] == 0x0A) // Digital In data
{
if (data[i+1] == 0x01)
lblDigitalIn.stringValue = #"HIGH";
else
lblDigitalIn.stringValue = #"LOW";
}
else if (data[i] == 0x0B) // Analog In data
{
UInt16 Value;
Value = data[i+2] | data[i+1] << 8;
lblAnalogIn.stringValue = [NSString stringWithFormat:#"%d", Value];
}
}
}
It seems that the problem are "float" or converted "int" values and if someone could help me to solve this problem I would be really happy!
All characteristic data is just bytes. Once a characteristic's data has been read it is up to the central app to convert the data to an appropriate format (as described by the peripheral's manufacturer or some characteristics also contain a format descriptor which will describe out to format its data.)

OSX / iOS: Reading a .WAV into a float buffer

I've got a ~1s mono .WAV on disk. I would like my OSX (and later iOS) app to read it into a float buffer.
What's the simplest way to achieve this?
Solution is to use ExtAudioFile()
I found it reading the most excellent Core-Audio bible
The libsndfile way :)
SF_INFO sfinfo;
SNDFILE *sf;
float *buf;
int err_code;
sfinfo.format = 0;
sf = sf_open("/meow.wav", SFM_READ, &sfinfo);
err_code = sf_error(sf);
if (err_code == SF_ERR_NO_ERROR) {
buf = malloc(sfinfo.frames * sfinfo.channels * sizeof(float));
sf_read(sf, buf, sfinfo.frames * sfinfo.channels);
printf("Done!\n");
} else {
printf("%s\n", sf_error_number(err_code));
}

Is there any easy way to make GPS coordinates coarse?

I'm working on an iPhone app that is using GPS coordinates for leaderboards. I don't need the coordinates to be exact --- actually I don't ever want the coordinates to be exact, to protect user privacy.
I am specifying kCLLocationAccuracyThreeKilometers for desiredAccuracy, but when the GPS is active it seems it can also pick up the exact location when the device has it.
QUESTION: Is there any easy algorithm I can use to make the GPS data more coarse? Say, make it granular to 3km.
If I just scale the numbers up and remove decimal points and scale them down again it will make it more coarse in some parts of the world than others.
Thanks!
While Mark's answer above was a useful, it still did not yield a formula with consistent results because it relied on a random number generator.
My buddy provided the best answer for this:
Round the lat,lon to the nearest significant figure depending on the granularity, but this would result in all the lat/lons near a certain location, wind up in the same location. This method would use the distance between two points in lat/lon to calculate the rounding of the lat lon. Use the same formula below and set the course to 0, then the distance is your distance granularity. Calculate the resulting new lat/lon subtract the two lat/lons to get the rounding amount for lat. Then set the heading to 90 and recalculate and subtract the new lat/lon from the old to get the rounding amount for lon.
And here's the C++ code:
class LocationUtility
{
public: static Location getLocationNow()
{
Location location;
if(context != null)
{
double latitude = 0;
double longitude = 0;
::of_getCurrentLocation(&latitude, &longitude);
location.setLatitude(latitude);
location.setLongitude(longitude);
location = makeLocationCoarse(location);
}
return location;
}
public: static Location makeLocationCoarse(const Location& location)
{
double granularityInMeters = 3 * 1000;
return makeLocationCoarse(location, granularityInMeters);
}
public: static Location makeLocationCoarse(const Location& location,
double granularityInMeters)
{
Location courseLocation;
if(location.getLatitude() == (double)0 &&
location.getLongitude() == (double)0)
{
// Special marker, don't bother.
}
else
{
double granularityLat = 0;
double granularityLon = 0;
{
// Calculate granularityLat
{
double angleUpInRadians = 0;
Location newLocationUp = getLocationOffsetBy(location,
granularityInMeters, angleUpInRadians);
granularityLat = location.getLatitude() -
newLocationUp.getLatitude();
if(granularityLat < (double)0)
{
granularityLat = -granularityLat;
}
}
// Calculate granularityLon
{
double angleRightInRadians = 1.57079633;
Location newLocationRight = getLocationOffsetBy(location,
granularityInMeters, angleRightInRadians);
granularityLon = location.getLongitude() -
newLocationRight.getLongitude();
if(granularityLon < (double)0)
{
granularityLon = -granularityLon;
}
}
}
double courseLatitude = location.getLatitude();
double courseLongitude = location.getLongitude();
{
if(granularityLon == (double)0 || granularityLat == (double)0)
{
courseLatitude = 0;
courseLongitude = 0;
}
else
{
courseLatitude = (int)(courseLatitude / granularityLat) *
granularityLat;
courseLongitude = (int)(courseLongitude / granularityLon) *
granularityLon;
}
}
courseLocation.setLatitude(courseLatitude);
courseLocation.setLongitude(courseLongitude);
}
return courseLocation;
}
// http://www.movable-type.co.uk/scripts/latlong.html
private: static Location getLocationOffsetBy(const Location& location,
double offsetInMeters, double angleInRadians)
{
Location newLocation;
double lat1 = location.getLatitude();
double lon1 = location.getLongitude();
lat1 = deg2rad(lat1);
lon1 = deg2rad(lon1);
double distanceKm = offsetInMeters / (double)1000;
const double earthRadiusKm = 6371;
double lat2 = asin( sin(lat1)*cos(distanceKm/earthRadiusKm) +
cos(lat1)*sin(distanceKm/earthRadiusKm)*cos(angleInRadians) );
double lon2 = lon1 +
atan2(sin(angleInRadians)*sin(distanceKm/earthRadiusKm)*cos(lat1),
cos(distanceKm/earthRadiusKm)-sin(lat1)*sin(lat2));
lat2 = rad2deg(lat2);
lon2 = rad2deg(lon2);
newLocation.setLatitude(lat2);
newLocation.setLongitude(lon2);
return newLocation;
}
private: static double rad2deg(double radians)
{
static double ratio = (double)(180.0 / 3.141592653589793238);
return radians * ratio;
}
private: static double deg2rad(double radians)
{
static double ratio = (double)(180.0 / 3.141592653589793238);
return radians / ratio;
}
/*
public: static void testCoarse()
{
Location vancouver(49.2445, -123.099146);
Location vancouver2 = makeLocationCoarse(vancouver);
Location korea(37.423938, 126.692488);
Location korea2 = makeLocationCoarse(korea);
Location hiroshima(34.3937, 132.464);
Location hiroshima2 = makeLocationCoarse(hiroshima);
Location zagreb(45.791958, 15.935786);
Location zagreb2 = makeLocationCoarse(zagreb);
Location anchorage(61.367778, -149.900208);
Location anchorage2 = makeLocationCoarse(anchorage);
}*/
};
This is very similar to a previous question
Rounding Lat and Long to Show Approximate Location in Google Maps
If you assume the earth is a sphere (probably adequate for this problem), then you just need to calculate a location which is a certain angular distance from the given latitude and longitude. Pick a distance and a (random) direction, and calculate the new location by using the distance formula.
There's good discussion of the opposite problem (distance between two latitude/longitude points) here: Link
It ought to be relatively straightforward to go from there to finding a point a specified distance away from the given point.
The answer from swinefeaster is ok, but there is no need for such complex maths. If you're rounding to a grid, then the latitude changes by constant amounts at all points on the planet. Longitude changes by different amounts according to how far you are from the equator.
The following code snaps latitude and longitude to an arbitrary grid size
double EARTH_RADIUS_KM = 6371;
double GRID_SIZE_KM = 1.6; // <----- Our grid size in km..
double DEGREES_LAT_GRID = Math.toDegrees(GRID_SIZE_KM / EARTH_RADIUS_KM);
// ^^^^^^ This is constant for a given grid size.
public Location snapToGrid(Location myLoc) {
double cos = Math.cos(Math.toRadians(myLoc.latitude));
double degreesLonGrid = DEGREES_LAT_GRID / cos;
return new Location (
Math.round(myLoc.longitude / degreesLonGrid) * degreesLonGrid,
Math.round(myLoc.latitude / DEGREES_LAT_GRID) * DEGREES_LAT_GRID);
}
Note that this will fail in the case where you are at the Pole (when the cos function approaches zero). Depending on your grid size, the results become unpredictable as you approach a latitude of +/- 90 degrees. Handling this is an exercise left for the reader :)
I try to implemente the solution in Ruby but in my case, the coarse coordinate vs real have a huge difference. the coarse coordinate only change when the lat change but when lat stay the same and long move, coarse remain the same. In case someone can check the code below, perhaps I made a bad coding.
class CoarseLocation
AREA_LIMIT = 1000
class << self
def make_location_coarse(lat, lon)
if lat.nil? && lon.nil?
raise InvalidParamsError
end
location = [lat.to_f, lat.to_f]
new_location_up = get_location_by_offset(location, AREA_LIMIT, 0)
granularityLat = location[0] - new_location_up[0]
if granularityLat < 0
granularityLat = -granularityLat
end
new_location_right = get_location_by_offset(location, AREA_LIMIT, 1.57079633)
granularityLon = location[1] - new_location_right[1]
if(granularityLon < 0)
granularityLon = -granularityLon
end
course_lat = location[0]
course_lon = location[1]
if(granularityLat == 0.0) || (granularityLon == 0.0)
course_lat = 0
course_lon = 0
else
course_lat = (course_lat / granularityLat).to_i * granularityLat
course_lon = (course_lon / granularityLon).to_i * granularityLon
end
[course_lat, course_lon]
end
def get_location_by_offset(location, offset, angle)
lat_radius = location[0] * Math::PI / 180
lon_radius = location[1] * Math::PI / 180
distance = (offset / 1000).to_f
earth_radius = 6371
lat_radius_1 = (Math::asin( Math::sin(lat_radius) * Math::cos(distance/earth_radius) + Math::cos(lat_radius) * Math::sin(distance/earth_radius) * Math::cos(angle))).to_f
lon_radius_1 = (lon_radius + Math::atan2(Math::sin(angle)*Math::sin(distance/earth_radius)*Math::cos(lat_radius), Math::cos(distance/earth_radius) - Math::sin(lat_radius)*Math::sin(lat_radius_1))).to_f
new_lat = lat_radius_1 * 180 / Math::PI
new_lon = lon_radius_1 * 180 / Math::PI
return [new_lat.to_f, new_lon.to_f]
end
end
end
Location field is always ab array of 2 elements in which [0] is lat and [1] is long.

Resources