none
How to save GPS info to an image's Properties

    Question

  • Dear All,

    I am trying to save GPS information to an image, but I 've got an error message: "The parameter is incorrect. WinRT information: Cannot write to the specified property (System.GPS.Latitude). The property is read-only."

    Could you please help me to find out what's the wrong with my source code?

    And is System.GPS.Latitude a read-only property? How can I change the property to read/write?

    double GPSLatitude = 25.125965;
    
    List<string> propertiesName = new List<string>();
    propertiesName.Add("System.GPS.Latitude");
    IDictionary<string, object> extraProperties = await photoStorageFile.Properties.RetrievePropertiesAsync(propertiesName);
    extraProperties.Add("System.GPS.Latitude", GPSLatitude);
    await photoStorageFile.Properties.SavePropertiesAsync(extraProperties);

    Thanks,

    Angie



    • Edited by Angie_ Friday, March 23, 2012 2:39 AM
    Thursday, March 22, 2012 4:26 AM

Answers

  • When I looked at the docs for System.GPS.Latitude I noticed that it is calculated from System.GPS.LatitudeNumerator and System.GPS.LatitudeDenominator. Because it is calculated it can't be set directly, but I verified that you can set those and the final value will be calculated appropriately:

                int[] latitudeNumerator = new int[3] { 47, 38, 24471 };
                int[] latitudeDenominator = new int[3] { 1, 1 , 1000 };
                string latitudeRef = "N";
                int[] longitudeNumerator = new int[3] { 122, 7, 470316 };
                int[] longitudeDenominator = new int[3] { 1, 1, 10000 };
                string longitudeRef = "W";
               
                List<KeyValuePair<string, object>> extraProperties = new List<KeyValuePair<string, object>>();
                extraProperties.Add(new KeyValuePair<string, object>("System.GPS.LongitudeNumerator", longitudeNumerator));
                extraProperties.Add(new KeyValuePair<string, object>("System.GPS.LongitudeDenominator", longitudeDenominator));
                extraProperties.Add(new KeyValuePair<string, object>("System.GPS.LongitudeRef", longitudeRef));
    
                extraProperties.Add(new KeyValuePair<string, object>("System.GPS.LatitudeNumerator", latitudeNumerator));
                extraProperties.Add(new KeyValuePair<string, object>("System.GPS.LatitudeDenominator", latitudeDenominator));
                extraProperties.Add(new KeyValuePair<string, object>("System.GPS.LatitudeRef", latitudeRef));
    
                extraProperties.Add(new KeyValuePair<string, object>("System.Title", "Sample Title"));
    
                await photoStorageFile.Properties.SavePropertiesAsync(extraProperties);
    --Rob


    Monday, March 26, 2012 9:36 PM
  • Hi Angie,

    I don't know why the property system was defined to use degrees/minutes/seconds (DMS), but since that is what it uses that is what we need to pass in.

    You will need to convert your decimal value into DMS and then represent those values as fractions with the numerator and denominator passed in separately. Here is an algorithm for doing the conversion from decimal degrees to DMS.

    For 25.125965:

    Degrees = 25
    Minutes = 60 * .125965 = 7.55790 => 7
    Seconds = 60 * .5579 = 33.4740

    So 25.125965 is 25° 7' 33.474"

    For numerators we have: 25, 7, 33474
    For denominators we have: 1,1,1000

    25/1 = 25 degrees
    7/1 = 7 minutes
    33474/1000 = 33.474 seconds

    --Rob

    • Marked as answer by Angie_ Friday, April 20, 2012 2:55 AM
    • Unmarked as answer by Angie_ Friday, April 20, 2012 3:37 AM
    • Marked as answer by Angie_ Friday, April 20, 2012 3:54 AM
    Friday, April 20, 2012 12:36 AM

All replies

  • Angie - I think there are a couple of issues here:

    1) If System.GPS.Latitude is in a Microsoft-written dll, and is marked as read-only, you can't change its value or make it read/write.  It sounds to me that it's simply reporting the current latitude of the GPS, not allowing you to set it because it wouldn't be correct at your current location.  I don't really understand what you're trying to do.  The documentation (which I've never seen previous to this question) does not indicate that it's read/write or just read, but it does say "indicates latitude" (http://msdn.microsoft.com/en-us/library/windows/desktop/bb760543(v=vs.85).aspx), instead of "gets/sets latitude". 

    2) System.GPS.Latitude does not appear in the Metro API.  You might be able to create an application which uses it but it's certainly not going to pass Store certification.  You probably want to try http://msdn.microsoft.com/en-us/library/windows/apps/windows.devices.geolocation.aspx.


    Matt Small - Microsoft Escalation Engineer - Forum Moderator

    Thursday, March 22, 2012 1:36 PM
  • I work with VB instead of C# so i'm not sure of your exact issue but I was pretty sure they already had properties for longitude/latitude for a file built in, however these properties may not be available directly with the built in classes, i'm not sure...

    I will try & see if I can show you some properties that exist to change & how to read those properties later if you want but since I don't work in C# about the most I can do is post the converted C# code or the VB code & let you do the conversion & such.

    Thursday, March 22, 2012 1:51 PM
  • Dear Matt and Michael,

    I'm trying to do this because I want to save the GPS coordinate information into an image's extraProperties after taking a photo. What confuse me is that I don't know which properties for longitude/latitude for a file(image) should I use. 

    I've seen http://msdn.microsoft.com/en-us/library/windows/apps/windows.devices.geolocation.aspx. before and have use that API to get the GPS information. Here is my source code:

    double GPSLatitude = 0.0;
    
    Geolocator geo = new Geolocator();
    Geoposition pos = await geo.GetGeopositionAsync();
    GPSLatitude = pos.Coordinate.Latitude;
    
    List<string> propertiesName = new List<string>();
    propertiesName.Add("System.GPS.Latitude");
    IDictionary<string, object> extraProperties = await photoStorageFile.Properties.RetrievePropertiesAsync(propertiesName);
    extraProperties.Add("System.GPS.Latitude", GPSLatitude);
    await photoStorageFile.Properties.SavePropertiesAsync(extraProperties);

    Could you please show me a way to implement this?

    Thanks,

    Angie

    Thursday, March 22, 2012 2:05 PM
  • Regular properties, extra properties, the files properties itself, it hardly matters what you use but it all needs to be used where the properties already exist so that other programs will be able to read the properties they need without needing to understand custom properties which people have put on to a file themselves...

    The properties i'm talking about already existing for longitude/latitude however are FILE properties, i'm not sure what properties extraProperties sets or just normal old Properties functions, maybe they are file properties & possibly not all of them or maybe it's I don't know, but I will show you how to read & write such a property for longitude/latitude which I am pretty sure already exist...it will take a few minutes for me to work it into metro though & more time to find a solution for C# since I work with VB myself.

    Thursday, March 22, 2012 2:37 PM
  • I think you might be able to very easily port the ExifLib to WinRT and get access to these properties.

    Filip Skakun

    Thursday, March 22, 2012 6:30 PM
  • Dear Michael,

    Do you have any further suggestions?

    Thanks,

    Angie

    Monday, March 26, 2012 6:20 AM
  • I am looking at a pictures file properties that can be set right now, the only properties to do with location at all is "By Location" & "Location", which i'm not sure what type of data they are meant to hold or how it's meant to be formatted....but maybe you could use it to store/read gps location...

    I am working right now to determine if the code you use to work with the properties right now reads/writes these properties I am viewing now from a desktop app, and converting the code to metro to make sure that metro apps use the same properties or not.

    -I should be able to get a working code for you to store/read the gps information within 24hrs.  I just need to do more testing first because I haven't messed with file properties from within metro, reading or storing such info.

    NOTE:I am reading the properties for a .png picture property that is able to be set...if you work with another image format I need the format(s) so I can determine how to do it for each type, if it differs at all for any used file type(s).


    Monday, March 26, 2012 9:30 AM
  • Hi Angie_,

    The major problem you have is that it looks like you're passing single numeric values for latitude, but System.GPS.Latitude expects an array of doubles: degrees, minutes, seconds.

    That said, there appears to be a problem with Latitude and Longitude that I'm still trying to track down.

    Here is code which demonstrates how to set properties; however it isn't working for Latitude or Longitude:

             List<KeyValuePair<string, object>> extraProperties = new List<KeyValuePair<string, object>>();
             extraProperties.Add(new KeyValuePair<string, object>("System.GPS.Latitude", ToDegMinSec( pos.Coordinate.Latitude)));
             extraProperties.Add(new KeyValuePair<string, object>("System.GPS.Longitude", ToDegMinSec(pos.Coordinate.Longitude)));
             extraProperties.Add(new KeyValuePair<string, object>("System.Title", "Sample Title"));
    
             await photoStorageFile.Properties.SavePropertiesAsync(extraProperties);

    The JavaScript Simple imaging sample demonstrates this as well (but in JavaScript syntax). It also fails to save the Latitude and Longitude successfully.

    --Rob

    Monday, March 26, 2012 8:28 PM
  • Michael, it sounds like you're heading down the wrong track here. Metro style apps cannot call the desktop property system API but need to use code similar to what Angie was using (and see my previous post).

    That you're concerned with different files storing properties differently in different formats suggests that you're trying to bypass Windows' properties system and interpret the files directly. That's really not recommended and won't scale well. If you use the properties system then your app doesn't need to know the internals of different file types.

    --Rob

    Monday, March 26, 2012 8:35 PM
  • When I looked at the docs for System.GPS.Latitude I noticed that it is calculated from System.GPS.LatitudeNumerator and System.GPS.LatitudeDenominator. Because it is calculated it can't be set directly, but I verified that you can set those and the final value will be calculated appropriately:

                int[] latitudeNumerator = new int[3] { 47, 38, 24471 };
                int[] latitudeDenominator = new int[3] { 1, 1 , 1000 };
                string latitudeRef = "N";
                int[] longitudeNumerator = new int[3] { 122, 7, 470316 };
                int[] longitudeDenominator = new int[3] { 1, 1, 10000 };
                string longitudeRef = "W";
               
                List<KeyValuePair<string, object>> extraProperties = new List<KeyValuePair<string, object>>();
                extraProperties.Add(new KeyValuePair<string, object>("System.GPS.LongitudeNumerator", longitudeNumerator));
                extraProperties.Add(new KeyValuePair<string, object>("System.GPS.LongitudeDenominator", longitudeDenominator));
                extraProperties.Add(new KeyValuePair<string, object>("System.GPS.LongitudeRef", longitudeRef));
    
                extraProperties.Add(new KeyValuePair<string, object>("System.GPS.LatitudeNumerator", latitudeNumerator));
                extraProperties.Add(new KeyValuePair<string, object>("System.GPS.LatitudeDenominator", latitudeDenominator));
                extraProperties.Add(new KeyValuePair<string, object>("System.GPS.LatitudeRef", latitudeRef));
    
                extraProperties.Add(new KeyValuePair<string, object>("System.Title", "Sample Title"));
    
                await photoStorageFile.Properties.SavePropertiesAsync(extraProperties);
    --Rob


    Monday, March 26, 2012 9:36 PM
  • Dear Rob,

    Sorry for the late reply and So Sorry for the action of "Unmark As Answer"..

    Because there are still some questions that confuse me:

    int[] latitudeNumerator = new int[3] { 47, 38, 24471 }; int[] latitudeDenominator = new int[3] { 1, 1 , 1000 }; string latitudeRef = "N"; int[] longitudeNumerator = new int[3] { 122, 7, 470316 }; int[] longitudeDenominator = new int[3] { 1, 1, 10000 }; string longitudeRef = "W";

    When I try to add GPS info to an image, will the value of "latitudeDenominator[] and "latitudeRef"  always be "{1, 1 , 1000}" and "N"?

    double GPSLatitude = 0.0;

    Geolocator geo = new Geolocator();
    Geoposition pos = await geo.GetGeopositionAsync();
    GPSLatitude = pos.Coordinate.Latitude;

    Can I set the value of latitudeNumerator[0] to "double[]" instead of "int[]"? (like GPSLatitude  illustrated above)

    And can I set the value of latitudeNumerator[1] and latitudeNumerator[2] to "0" ?

    Thanks~

    Angie





    • Edited by Angie_ Friday, April 06, 2012 10:33 AM
    Friday, April 06, 2012 3:40 AM
  • The numerator and denominator are always ints, with the three values being the degrees, minutes, and seconds. You cannot set a double directly: it is always calculated for each value separately as numerator / denominator.

    You can use whatever int values you like for the Denominator. {1, 1, 10^X } are easy to work with and I would recommend using them. "N" is for North of the equator, "S" is for South.

    --Rob

    Tuesday, April 10, 2012 3:29 AM
  • Dear Rob,

    Why should we have to save time value(minutes, seconds) to GPS info?

    And I still don't know how to work with numerator and denominator...

    I've tried the code below, and expect to save info "GPS Latitude=> 25.125965: 1: 1" to GPS, but the result info that has been saved is "GPS Latitude=> 25: 8: 34.474000000001972"

                int[] latitudeNumerator = new int[3] { 25125965, 1, 1 };
                int[] latitudeDenominator = new int[3] { 1000000, 1 , 1 };
    Could you please show me how to save the double value if the GPSLatitude is 25.125965?

    Thanks,

    Angie


    • Edited by Angie_ Friday, April 13, 2012 3:07 AM
    Friday, April 13, 2012 3:06 AM
  • Hi Angie,

    I don't know why the property system was defined to use degrees/minutes/seconds (DMS), but since that is what it uses that is what we need to pass in.

    You will need to convert your decimal value into DMS and then represent those values as fractions with the numerator and denominator passed in separately. Here is an algorithm for doing the conversion from decimal degrees to DMS.

    For 25.125965:

    Degrees = 25
    Minutes = 60 * .125965 = 7.55790 => 7
    Seconds = 60 * .5579 = 33.4740

    So 25.125965 is 25° 7' 33.474"

    For numerators we have: 25, 7, 33474
    For denominators we have: 1,1,1000

    25/1 = 25 degrees
    7/1 = 7 minutes
    33474/1000 = 33.474 seconds

    --Rob

    • Marked as answer by Angie_ Friday, April 20, 2012 2:55 AM
    • Unmarked as answer by Angie_ Friday, April 20, 2012 3:37 AM
    • Marked as answer by Angie_ Friday, April 20, 2012 3:54 AM
    Friday, April 20, 2012 12:36 AM
  • Dear Rob

    Thanks for your help! :)

    Angie



    • Edited by Angie_ Friday, April 20, 2012 3:54 AM
    Friday, April 20, 2012 2:55 AM
  • I'm trying to achieve the same result, but using a BitmapEncoder.
    The Latitude works fine but I just can't seem to get the Longitude to write to the file correctly.
    (All the other properties are working fine.)

    The inputStream is from the webcam via a capture control.
    Pretty sure the decoder would behave the same if inputStream was from an StorageFile though.

    Any ideas please?

    Windows.Storage.Streams.IRandomAccessStream outputStream = await imageFile.OpenAsync(FileAccessMode.ReadWrite);
    var decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(inputStream);
    BitmapEncoder encoder = await Windows.Graphics.Imaging.BitmapEncoder.CreateForTranscodingAsync(outputStream, decoder);
    BitmapPropertySet bitmapPropSet = new BitmapPropertySet();
    
    bitmapPropSet.Add("System.Subject", new BitmapTypedValue(txtPhotoSubject.Text.Trim(), PropertyType.String));
    bitmapPropSet.Add("System.GPS.LatitudeNumerator", new BitmapTypedValue(new int[3] { 47, 38, 24471 }, PropertyType.Int32Array));
    bitmapPropSet.Add("System.GPS.LatitudeDenominator", new BitmapTypedValue(new int[3] { 1, 1, 1000 }, PropertyType.Int32Array));
    bitmapPropSet.Add("System.GPS.LatitudeRef", new BitmapTypedValue("E", PropertyType.String));
    bitmapPropSet.Add("System.GPS.LongitudeNumerator", new BitmapTypedValue(new int[3] { 122, 7, 470316 }, PropertyType.Int32Array));
    bitmapPropSet.Add("System.GPS.LongitudeDenominator", new BitmapTypedValue(new int[3] { 1, 1, 10000 }, PropertyType.Int32Array));
    bitmapPropSet.Add("System.GPS.LongitudeRef", new BitmapTypedValue("N", PropertyType.String));
    
    await encoder.BitmapProperties.SetPropertiesAsync(bitmapPropSet);                
    await encoder.FlushAsync();
    Here's the resulting properties...





    EDIT (2nd Oct 2013)
    Only thing that seems to fix the issue is to not include the LongitudeDenominator property.
    Commenting out that line works ok.
    I also needed to leave the LatitudeDenominator as 1,1,1 and just include the whole seconds.
    Close enough.
    • Edited by RD8388 Wednesday, October 02, 2013 3:45 PM
    Tuesday, October 01, 2013 3:48 PM
  • Hi,could you please tell me how to retrieve the Gps properties from the image;

    I tried using

     

                    StorageFile imageFile = await picker.PickSingleFileAsync();
                    var props = await imageFile.Properties.GetImagePropertiesAsync();

    but iam getting different values:

    Tuesday, December 31, 2013 6:03 AM