xamarin.iosxamarin.androidmvvmcrossgeotagging

Adding Geo tag info to images and upload to server


DESCRIPTION: With two days before deadline I find out and confirmed by Stuart that picture taken with TakePicture method does not save the Geo location.

WORKS:
1. The picture is taken with TakePicture and it's saved
2. The lat and lng are detected and sent to each platform implementation
3. The fileName is sent from core after picture is saved to implementations
4. The platform implementations are including the Geo Tags into the picture
5. The image upload works, sending the geo tags

THE CODE WHICH ADDS THE GEO TAGGING TO PICTURE :

ANDROID (UPDATE: WORKING):

public bool SaveImageWithGpsTags(string fileName, double lat, double lng)
{
   var context = Mvx.Resolve<IMvxAndroidGlobals>().ApplicationContext;
   var fullPath = Path.Combine(context.FilesDir.Path, fileName);

   if (!File.Exists(fullPath)) return false;
   try
   {
       using (var ef = new ExifInterface(fullPath))
       {
          ef.SetAttribute(ExifInterface.TagGpsLatitude, Dec2Dms(lat));
          ef.SetAttribute(ExifInterface.TagGpsLongitude, Dec2Dms(lng));
          ef.SetAttribute(ExifInterface.TagGpsLatitudeRef, lat > 0 ? "N" : "S");
          ef.SetAttribute(ExifInterface.TagGpsLongitudeRef, lng > 0 ? "E" : "W");
          ef.SaveAttributes();
       }
   }
   catch (Exception e)
   {
       return false;
   }
}

static String Dec2Dms(double coord)
{
    coord = coord > 0 ? coord : -coord;  // -105.9876543 -> 105.9876543
    var sOut = string.Format("{0}/1,", ((int)coord));   // 105/1,
    coord = (coord % 1) * 60;         // .987654321 * 60 = 59.259258
    sOut = sOut + string.Format("{0}/1,", ((int)coord));   // 105/1,59/1,
    coord = (coord % 1) * 60000;             // .259258 * 60000 = 15555
    sOut = sOut + string.Format("{0}/1000,", ((int)coord));   // 105/1,59/1,15555/1000
    return sOut;
}

TOUCH (UPDATE: WORKING):

   const string resScheme = "res:";
   var imagePath = fileName.StartsWith(resScheme) ? fileName.Substring(resScheme.Length) : Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), fileName);
   var originalImage = UIImage.FromFile(imagePath);
   var gpsDict = new NSMutableDictionary();
   var imageMetadata = new NSMutableDictionary();

   gpsDict.SetValueForKey(NSObject.FromObject(lng), new NSString("Longitude"));
   gpsDict.SetValueForKey(NSObject.FromObject(lng > 0 ? "E" : "W"), new NSString("LongitudeRef"));
   gpsDict.SetValueForKey(NSObject.FromObject(lat), new NSString("Latitude"));
   gpsDict.SetValueForKey(NSObject.FromObject(lat > 0 ? "N" : "S"), new NSString("LatitudeRef"));
   gpsDict.SetValueForKey(NSObject.FromObject(DateTime.UtcNow.ToString("HH:MM:ss.ff")), new NSString("TimeStamp"));
   imageMetadata.SetValueForKey (gpsDict as NSDictionary, MonoTouch.ImageIO.CGImageProperties.GPSDictionary);

   var imgSrc = CGImageSource.FromData (originalImage.AsJPEG ());
   var outImageData = new NSMutableData();

   using (
        var d = CGImageDestination.FromData(outImageData, imgSrc.TypeIdentifier, 1,
                new CGImageDestinationOptions()))
        {
            d.AddImage (imgSrc, imgSrc.ImageCount - 1, imageMetadata);
            d.Close();
        }
  NSError writeError;
  var imageSaved = outImageData.Save(imagePath, NSDataWritingOptions.Atomic, out writeError);



UPDATE: (UPLOAD WORKS NOW!):
Here is the code which sends the image:

if (client.DefaultRequestHeaders.CacheControl == null)
    client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue();

client.DefaultRequestHeaders.CacheControl.NoCache = true;
client.DefaultRequestHeaders.CacheControl.NoStore = true;

byte[] imageBytes;
var result = Mvx.Resolve<IMvxFileStore>().TryReadBinaryFile(imagePath, out imageBytes);
var fileContent = new ByteArrayContent(imageBytes,0,imageBytes.Count());
var fileName = NewGuid() + ".jpg";
const string reference = "picture"
fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
    FileName = fileName,
    Name = reference,
};
content.Add(fileContent);
content.Add(new StringContent(Settings.UserId), "userid");
await client.PostAsync("SOME SERVER URL", content);

Solution

  • Everyone who is interested in the solution will find the answer in the question itself updated!

    Sorry Cheesebaron, I didn't know!