DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
View Events Video Library
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Integrating PostgreSQL Databases with ANF: Join this workshop to learn how to create a PostgreSQL server using Instaclustr’s managed service

Mobile Database Essentials: Assess data needs, storage requirements, and more when leveraging databases for cloud and edge applications.

Monitoring and Observability for LLMs: Datadog and Google Cloud discuss how to achieve optimal AI model performance.

Automated Testing: The latest on architecture, TDD, and the benefits of AI and low-code tools.

Related

  • Designing Web Apps for High Availability in AWS
  • Top React Libraries for Data-Driven Dashboard App Development
  • Beginners Guide for Web Scraping Using Selenium
  • Visually Designing Views for Java Web Apps

Trending

  • A Better Web3 Experience: Account Abstraction From Flow (Part 2)
  • Exploring Edge Computing: Delving Into Amazon and Facebook Use Cases
  • Unleashing the Power of Microservices With Spring Cloud
  • The Systemic Process of Debugging
  1. DZone
  2. Data Engineering
  3. Data
  4. Ionic/Cordova Demo: Where Did I Take That Picture?

Ionic/Cordova Demo: Where Did I Take That Picture?

Ever take a picture on your phone of something to remember later, but need to know where that pic was snapped? Check out this cool Ionic/Cordova demo.

Raymond Camden user avatar by
Raymond Camden
·
Dec. 11, 15 · Analysis
Like (4)
Save
Tweet
Share
5.12K Views

Join the DZone community and get the full member experience.

Join For Free

every now and then i think of an idea for a cool (aka useless and pointless but fun) app that i think will take me one hour and let me grow my small little empire of demos. sometimes those “quick little demos,” end up turning into multi-hour sessions as i pull my hair out trying to find out why this or that isn’t working. that’s frustrating as heck while i’m working on it, but in the end it makes me as happy.

smile-kitten-large

why? because if i run into problems with my little “toy” demo, most likely you, the poor reader who has to put up with my silly demos, will run into it in a production app. and if my pain helps you avoid issues, then this blog will earn its keep. ok, so what was the idea?

a few weeks ago i was shopping with my wife. it was the type of store where pretty much nothing in it interests me so i was just kind of mindlessly following along. but when my wife pointed out something she liked, i discretely snapped a picture of the item so i’d remember it as a possible present for her birthday or christmas. unfortunately, i couldn’t remember the name of the store. i knew roundabout where it was, of course, but not the actual store.

turns out that many pictures automatically include data that relates to the location where the picture was taken. you can – with a few clicks – get the latitude and longitude of the picture. that’s nice – but frankly, i can’t translate those values into a ‘real’ location off the top of my head. i’m sure web apps exist to help with that, but i thought, wouldn’t it be nice if i could just select a picture and have it tell me where it was taken – in english? for example:

shot1

for my demo, i decided to build the following:

  • let the user select a picture.
  • attempt to read the exif data and get a location.
  • try to foursquare the location. i figured that would work great for businesses.
  • if that fails, try to reverse geocode it to an address at least.
  • if that fails too, show it on a map at least.

right away i ran into some interesting issues. first, i needed to read the exif data. i found a cordova plugin for it, but it had not been updated in two years, and i saw multiple issues reported that were not being addressed. so then i simply googled for “exif javascript” and came across this project: exif-js . this project was also old with outstanding prs, but i thought it might be safer to try.

for the most part, it just works. here is a snippet showing it in action:

$scope.selectpicture = function() {
navigator.camera.getpicture(gotpic, errhandler, {
sourcetype:camera.picturesourcetype.photolibrary,
destinationtype:camera.destinationtype.native_uri
});
};

var errhandler = function(e) {
alert('error with camera: '+e);
};

//utility funct based on https://en.wikipedia.org/wiki/geographic_coordinate_conversion
var convertdegtodec = function(arr) {
return (arr[0].numerator + arr[1].numerator/60 + (arr[2].numerator/arr[2].denominator)/3600).tofixed(4);
};

var gotpic = function(u) {
console.log('got image '+u);
$scope.img.url = u;
//scope.apply can kma
$scope.$apply();

};

var img = document.queryselector("#selimage");

img.addeventlistener("load", function() {
console.log("load event for image "+(new date()));
$scope.status.text = "loading exif data for image.";
exif.getdata(document.queryselector("#selimage"), function() {
console.log("in exif");

//console.dir(exif.getalltags(img));
var long = exif.gettag(img,"gpslongitude");
var lat = exif.gettag(img,"gpslatitude");
if(!long || !lat) {
$scope.status.text = "unfortunately, i can't find gps info for the picture";
return;
}
long = convertdegtodec(long);
lat = convertdegtodec(lat);
//handle w/s
if(exif.gettag(this,"gpslongituderef") === "w") long = -1 * long;
if(exif.gettag(this,"gpslatituderef") === "s") lat = -1 * lat;
console.log(long,lat);
locateaddress(long,lat);
});
}, false);

first thing i discovered was that when you select an image in cordova, the exif data is stripped down to about 4 or so different tags. turns out this is a known bug ( cf-1285 ) due to the fact that the plugin copies the original image and in that process removes the data. the bug is marked resolved, but obviously it isn’t. however, if you switch the camera source to native_uri then the problem goes away.

so far so good. to work with the code, you need to point it to an image in the dom, and wait for the image to finish loading. that by itself isn’t hard, although i feel dirty when i use the dom in angular controllers. (i got over it.) i then discovered an issue with the library. when it loads the exif data, it copies the values to the dom item for caching. i’m using the same image every time you select a new photo, so this meant the tag data was cached. i filed a bug report and in the meantime i simply edited the library to remove the cache check. that’s bad – but i got over that too.

the next thing i had to work with was the location stuff. as i said, the idea was to first check foursquare, fall back to reverse geocoding, and fall back again to a static map. let’s look at the controller code first.

var locateaddress = function(long,lat) {

$scope.status.text = "trying to locate the photo.";

location.getinfo(long, lat).then(function(result) {
console.log('result was '+json.stringify(result));
if(result.type === 'foursquare') {
$scope.status.text = 'your photo was taken at ' + result.name + ' located at ' + result.address;
} else if (result.type === 'geocode') {
$scope.status.text = 'your photo appears to have been taken at ' + result.address;
} else {
var map = 'https://maps.googleapis.com/maps/api/staticmap?center='+lat+','+long+'zoom=13&size=300x300&maptype=roadmap&markers=color:blue%7clabel:x%7c'+lat+','+long;
$scope.status.text = 'sorry, i\'ve got nothing. but here is a map!';
}
});
};

not too complex, right? i just run my service and deal with the result. the service is a bit complex, but really just makes use of the various apis i’m hitting.

angular.module('starter.services', [])

.factory('foursquare', function($http) {

var client_id = 'mahsecretismahsecret';
var client_secret = 'soylentgreenispeople';

function whatsat(long,lat) {
return $http.get('https://api.foursquare.com/v2/venues/search?ll='+lat+','+long+'&intent=browse&radius=30&client_id='+client_id+'&client_secret='+client_secret+'&v=20151201');
}

return {
whatsat:whatsat
};
})
.factory('geocode', function($http) {
var key = 'google should let me geocode for free';

function lookup(long,lat) {
return $http.get('https://maps.googleapis.com/maps/api/geocode/json?latlng='+lat+','+long+'&key='+key);
}

return {
lookup:lookup
};

})
.factory('location', function($q,foursquare,geocode) {

function getinfo(long,lat) {
console.log('ok, in getinfo with '+long+','+lat);
var deferred = $q.defer();
foursquare.whatsat(long,lat).then(function(result) {
//console.log('back from fq with '+json.stringify(result));
if(result.status === 200 && result.data.response.venues.length >= 1) {
var bestmatch = result.data.response.venues[0];
//convert the result to something the caller can use consistently
var result = {
type:"foursquare",
name:bestmatch.name,
address:bestmatch.location.formattedaddress.join(", ")
}
console.dir(bestmatch);
deferred.resolve(result);
} else {
//ok, time to try google
geocode.lookup(long,lat).then(function(result) {
console.log('back from google with ');
if(result.data && result.data.results && result.data.results.length >= 1) {
console.log('did i come in here?');
var bestmatch = result.data.results[0];
console.log(json.stringify(bestmatch));
var result = {
type:"geocode",
address:bestmatch.formatted_address
}
deferred.resolve(result);
}
});
}
});

return deferred.promise;
}
return {
getinfo:getinfo
};

});

in both cases, i’m assuming the first result from the api is the best result. that may not always be true, but it works for now. you’ve seen an example of foursquare working, here is an example of the reverse geocode.

geocode

and here it is with the last fallback. yes, this is the same picture, i just temporarily disabled the geocode service for a quick test.

map

all in all, this was a fun little app to build, and as i said, i’m glad i ran into the exif issues. i know i’ll need that in the future. you can find the complete source code for this demo here: https://github.com/cfjedimaster/cordova-examples/tree/master/photolocate

Data (computing) app Exif Web Service Web apps Cache (computing) Apache Cordova

Published at DZone with permission of Raymond Camden, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Designing Web Apps for High Availability in AWS
  • Top React Libraries for Data-Driven Dashboard App Development
  • Beginners Guide for Web Scraping Using Selenium
  • Visually Designing Views for Java Web Apps

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: