Olen tässä jo useamman tunnin koittanut saada selville miten tuo Jpeggien geotaggaus onnistuisi. Koittanu tuossa kirjastoja kuten 'ExifExtractor (http://www.codeproject.com/KB/graphics/exifextractor.aspx)' sekä 'ExifLibrary for .NET (http://www.codeproject.com/KB/graphics/ExifLibrary.aspx)', mutta kumminkaan siinä onnistumatta. ExifExtractorilla kyllä sain kuviin tallentumaan muita tietoja kuten Copyright ja artist, mutta Gps tietojen tallennus ei meinaa onnistua. Kuviin ei siis tule tietoihin sitä GPS kenttää, jossa on mm. leveysaste sekä pituusaste.
Tällä ExifExtractorilla exif tietojen tallennus tapahtuu seuraavasti:
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + ".jpg")); Goheer.EXIF.EXIFextractor er = new Goheer.EXIF.EXIFextractor(ref bmp, "\n"); er.setTag(getExifTagId("Artist"), username); er.setTag(getExifTagId("Copyright"), "Yritys Oy"); bmp.Save(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + "_en.jpg")); bmp.Dispose();
Tämä getExitTagId palauttaa exif tagin id:n, jonka uskosin olevan ihan toimiva, koska Copyright ja Artist tallentuu oikeisiin kohtiin.
private int getExifTagId(string tagstr) { Goheer.EXIF.translation tags = new Goheer.EXIF.translation(); foreach (DictionaryEntry s in tags) if ((string)s.Value == tagstr) return (int)s.Key; return 0; }
Yllä oleva koodi siis toimii niinkuin pitää, ongelma tulee kun yrittää tallentaa Gps tietoja.
Seuraavalla tavalla niitä olen yrittänyt tallentaa onnistumatta:
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + ".jpg")); Goheer.EXIF.EXIFextractor er = new Goheer.EXIF.EXIFextractor(ref bmp, "\n"); er.setTag(0x0000, "2");//gps ver er.setTag(0x0001, "N");//lat. ref er.setTag(0x0003, "E");//longitude. ref er.setTag(0x0002, latitude); er.setTag(0x0004, longitude); bmp.Save(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + "_en.jpg")); bmp.Dispose();
Edit: Nämäkin tiedot näyttää tallentuvan oikeisiin paikkoihin kun yhdellä viewer ohjelmalla katsoin, eli joku homma puuttuu kait siis joka estää sen että Gps tiedot alkaisi näkymään itse resurssien hallinnan tiedoissa.
Sivulta: http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/GPS.html löytyy nuo hexa idt mitä tossa käytän. Olen myös kokeillu samalla tapaa kuin aikaisemmissa esimerkeissä suoraa 'Gps Altitude' jne. jotka tulee tuosta ExifExtractorista, niin neki pitäisi toimia samanlailla, kun niillä samat idt näyttäis olevan.
Myös yrittänyt ilman ylimääräistä kirjastoa suoraan:
Image Pic = Image.FromFile(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + ".jpg")); PropertyItem propertyItem = Pic.PropertyItems[0]; propertyItem.Id = 0x0000; //index of the EXIF TAG propertyItem.Type = 0x3;// propertyItem.Len = 4; propertyItem.Value = new byte[4] {2,0,0,0}; Pic.SetPropertyItem(propertyItem); propertyItem.Id = 0x0002; //index of the EXIF TAG propertyItem.Type = 5;// propertyItem.Len = 4; propertyItem.Value = new byte[4]; Pic.SetPropertyItem(propertyItem); Pic.Save(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + "_pr.jpg"));
Tuosta en oo varma että miten tuo edes tallentaa nuo propertyItemit, että meneekö ne jotenki päällekkäin, no joka tapauksessa mitään tietoja ei tunnu tollakaan tallentuvan.
Edit:
No tuo property itemeiden tallennukset näyttäis toimivan shukot ok tuossa kun katsoin yhellä exif viewerillä. Siltikkään kuvan tiedoissa ei näy mitään GPS tietoja kylläkään.
Oiskohan jollan kokemusta tai tietämystä miten nuita Exif tietoja käsiteltäis tarkemmin tai Gps tietoja? Kyseessä on siis aina uusi kuva joka on juuri luotu base64-muodossa olevasta kuvasta(johon ei saa tallentumaan gps tietoja valmiiksi ainkaan tässä tapauksessa). Siinä on vain 2 exif-tagia valmiina jotka ovat: Chrominance Table, Luminance Table.
Lähti tuo hieman edistymään, kun muutti tallennuksen byte muotoon seuraavasti:
er.setTag(1, 2, 2, StrToByteArray("N")); //lat ref er.setTag(2, 24, 7, StrToByteArray("33°24'51.8")); //latitude er.setTag(3, 2, 2, StrToByteArray("E")); //long ref er.setTag(4, 24, 4, StrToByteArray("33°24'51.8")); //longitude er.setTag(16, 2, 2, StrToByteArray("T")); //Gps image direction ref er.setTag(36864, 4, 7, StrToByteArray("0221")); //exif version
public static byte[] StrToByteArray(string str) { System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding(); return encoding.GetBytes(str); }
er.setTag on (id, length, type, byte[])
Tuossa muoden tallentaminen toimii paitsi latituden ja longituden, ne pitäs saada tallennettua rational64u[3] muodossa, olisikohan jollain tietoa miten tämä mahdollisesti tapahtuisi kun tallennetaan byte taulukkoon?
Toisesta kuvasta kattonu mallia ja koittanu pistää tarvittavia exif tietoja, ei kyl vieläkään näy mitään Gpsään liittyvää ite resurssien hallinnan avulla, mutta exif viewererillä tosiaan näkyy.
Lähti toimiin ku tarpeeks päätä seinään hakkasi ja tyyppi muunnoksia pyöritteli. Latitude ja longitude häätyi perus muunnoksien jälkeen muuttaa rationaalisiksi luvuiksi ja sen jälkeen tavuiksi ja tallentaa. Tuossa osa koodista jos joillakin muilla tarvetta joskus:
System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + ".jpg")); Goheer.EXIF.EXIFextractor er = new Goheer.EXIF.EXIFextractor(ref bmp, "\n"); Coordinate cLongitude = new Coordinate(Convert.ToDouble(longitude, CultureInfo.InvariantCulture), CoordinatesPosition.N); Coordinate cLatitude = new Coordinate(Convert.ToDouble(latitude, CultureInfo.InvariantCulture), CoordinatesPosition.E); Rational longitudeDegrees = new Rational(cLongitude.Degrees,2); Rational longitudeMinutes = new Rational(cLongitude.Minutes,2); Rational longitudeSeconds = new Rational(cLongitude.Seconds,7); Rational latitudeDegrees = new Rational(cLatitude.Degrees, 2); Rational latitudeMinutes = new Rational(cLatitude.Minutes, 2); Rational latitudeSeconds = new Rational(cLatitude.Seconds, 7); byte[] latDegrees = BitConverter.GetBytes(latitudeDegrees.ToUlong()); byte[] latMinutes = BitConverter.GetBytes(latitudeMinutes.ToUlong()); byte[] latSeconds = BitConverter.GetBytes(latitudeSeconds.ToUlong()); byte[] latTot = new byte[24]; byte[] lotDegress = BitConverter.GetBytes(longitudeDegrees.ToUlong()); byte[] lotMinutes = BitConverter.GetBytes(longitudeMinutes.ToUlong()); byte[] lotSeconds = BitConverter.GetBytes(longitudeSeconds.ToUlong()); byte[] lotTot = new byte[24]; System.Buffer.BlockCopy(latDegrees, 0, latTot, 0, 8); System.Buffer.BlockCopy(latMinutes, 0, latTot, 8, 8); System.Buffer.BlockCopy(latSeconds, 0, latTot, 16, 8); System.Buffer.BlockCopy(lotDegress, 0, lotTot, 0, 8); System.Buffer.BlockCopy(lotMinutes, 0, lotTot, 8, 8); System.Buffer.BlockCopy(lotSeconds, 0, lotTot, 16, 8); er.setTag(2, 24, 5, latTot); //latitude er.setTag(4, 24, 5, lotTot); //longitude bmp.Save(Path.Combine(Server.MapPath("~/App_Data/uploads/images"), filename + "_en.jpg")); bmp.Dispose();
Tossa nuo 2 luokkaa jotka löyty kait http://www.codeproject.com sivuilta, mut nyt alhaalla tuo sivusto.
//Coordinate Class using System; public class Coordinate { public double Degrees { get; set; } public double Minutes { get; set; } public double Seconds { get; set; } public CoordinatesPosition Position { get; set; } public Coordinate() { } public Coordinate(double value, CoordinatesPosition position) { //sanity if (value < 0 && position == CoordinatesPosition.N) position = CoordinatesPosition.S; //sanity if (value < 0 && position == CoordinatesPosition.E) position = CoordinatesPosition.W; //sanity if (value > 0 && position == CoordinatesPosition.S) position = CoordinatesPosition.N; //sanity if (value > 0 && position == CoordinatesPosition.W) position = CoordinatesPosition.E; var decimalValue = Convert.ToDecimal(value); decimalValue = Math.Abs(decimalValue); var degrees = Decimal.Truncate(decimalValue); decimalValue = (decimalValue - degrees) * 60; var minutes = Decimal.Truncate(decimalValue); var seconds = (decimalValue - minutes) * 60; Degrees = Convert.ToDouble(degrees); Minutes = Convert.ToDouble(minutes); Seconds = Convert.ToDouble(seconds); Position = position; } public Coordinate(double degrees, double minutes, double seconds, CoordinatesPosition position) { Degrees = degrees; Minutes = minutes; Seconds = seconds; Position = position; } public double ToDouble() { var result = (Degrees) + (Minutes) / 60 + (Seconds) / 3600; return Position == CoordinatesPosition.W || Position == CoordinatesPosition.S ? -result : result; } public override string ToString() { return Degrees + "º " + Minutes + "' " + Seconds + "'' " + Position; } } public enum CoordinatesPosition { N, E, S, W } //Rational Class using System; public class Rational { private int numerator; private int denominator; /// <summary> /// Creates an Exif Rational /// </summary> /// <param name="numerator">The value you want to store in the Rational</param> /// <param name="accuracy">The number of decimal places of accuracy</param> public Rational(double numerator, int accuracy) { accuracy = (int)Math.Pow(10, accuracy); this.numerator = Convert.ToInt32(Math.Abs(numerator * accuracy)); this.denominator = accuracy; } /// <summary> /// Creates an Exif Rational /// </summary> /// <param name="data">A ulong typically read from exif metadata</param> public Rational(ulong data) { this.numerator = (int)(data & 0xFFFFFFFFL); this.denominator = (int)((data & 0xFFFFFFFF00000000L) >> 32); } /// <summary> /// Returns the Rational as a Ulong, typically used to write back to exif metadata /// </summary> /// <returns>Ulong</returns> public ulong ToUlong() { return ((ulong)this.numerator) | (((ulong)this.denominator) << 32); } /// <summary> /// Returns the Rational as an Integer /// </summary> /// <returns>Int</returns> public int ToInt() { return Convert.ToInt32(Math.Round(Convert.ToDouble(this.numerator) / Convert.ToDouble(this.denominator))); } /// <summary> /// Returns the Rational as a Double /// </summary> /// <returns>Double</returns> public double ToDouble() { return this.ToDouble(0); } /// <summary> /// Returns the Rational as a Double /// </summary> /// <returns>Double, accurate to the specified number of decimal places</returns> /// <param name="decimalPlaces">The number of decimal places of accuracy</param> public double ToDouble(int decimalPlaces) { return Math.Round(Convert.ToDouble(this.numerator) / Convert.ToDouble(this.denominator), decimalPlaces); } /// <summary> /// Returns the Rational as a string /// </summary> /// <returns>A string in the format numerator/denominator</returns> public new string ToString() { return this.numerator.ToString() + "/" + this.denominator.ToString(); } }
Varmaan pientä viimeistelyä saattaa vaatia tuo. Eka testeillä kuvan näkyminen kartalla heitti joku pari sataa metriä. Ja tällä hetkellä käyttää vain Northern ja Eastern sijaintia. Joten muut ei välttämättä toimi.
Aihe on jo aika vanha, joten et voi enää vastata siihen.