2012-05-10 15 views
9

Quello che voglio fare:
scattare una fotografia con la mia PictureActivity * e aggiungere EXIF ​​(geotag) dati
*: Implementazione SurfaceHolder.Callback e utilizzando Camerascrittura/Geotag JPEG (dati EXIF) in Android

Cosa non funziona:
Aggiungendo i dati GPS EXIF ​​

Quello che ho provato:
Utilizzando la ExifInterface e impostare manualmente Camera.Parameters (sia con i metodi specifici per l'impostazione GPS meta-dati e usando params.set(String, Value)).

sto caricando le immagini su Flickr utilizzando FlickrJ (sì, ho impostato Flickr per importare i dati GPS - altre immagini funzionano bene), tuttavia questo strumento dice anche non ci sono dati GPS nella EXIF: http://regex.info/exif.cgi

Cosa mi manca?

(Android 2.2, HTC Desire)

Edit:
- La fotocamera è impostata per Geotag photos: On
- Ho provato con le posizioni GPS fittizi hardcoded

Ecco il codice per i parametri di impostazione manuale (provato con e senza rimuovere prima i dati GPS, e come detto anche set(String, Value)):

@Override 
public void surfaceCreated(SurfaceHolder holder) { 
    mCamera = Camera.open();  

    Camera.Parameters p = mCamera.getParameters(); 
    p.setPreviewSize(p.getPreviewSize().width, p.getPreviewSize().height); 
    Log.e("PictureActivity", "EXIF: "+AGlanceLocationListener.getLatitude()); 
    p.removeGpsData(); 
    p.setGpsLatitude(AGlanceLocationListener.getLatitude()); 
    p.setGpsLongitude(AGlanceLocationListener.getLongitude()); 
    p.setGpsAltitude(AGlanceLocationListener.getAltitude()); 
    p.setGpsTimestamp(AGlanceLocationListener.getTime()); 
    mCamera.setParameters(p); 
} 

suo e è il codice per l'utilizzo del ExifInterface:

//Save EXIF location data to JPEG 
ExifInterface exif; 
try { 
    exif = new ExifInterface("/sdcard/DCIM/"+filename+".jpeg"); 
    exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, 
     String.valueOf(AGlanceLocationListener.getLatitude())); 

    exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, 
     String.valueOf(AGlanceLocationListener.getLongitude())); 

    exif.saveAttributes(); 

} catch (IOException e) { 
    Log.e("PictureActivity", e.getLocalizedMessage()); 
} 

Ecco il codice per scrivere il file JPEG alla SDCARD:

Camera.PictureCallback jpegCallback = new Camera.PictureCallback() { 
    public void onPictureTaken(byte[] imageData, Camera c) 
    { 
     //  Bitmap pic = BitmapFactory.decodeByteArray(imageData, 0, imageData.length); 

     String day = String.valueOf(Calendar.getInstance().getTime().getDay()); 
     String hour = String.valueOf(Calendar.getInstance().getTime().getHours()); 
     String minute = String.valueOf(Calendar.getInstance().getTime().getMinutes()); 
     String second = String.valueOf(Calendar.getInstance().getTime().getSeconds()); 

     filename = "Billede"+day+hour+minute+second; 

     try { 
      FileOutputStream fos = new FileOutputStream(new File("/sdcard/DCIM/"+filename+".jpeg")); 
      fos.write(imageData); 
      fos.flush(); 
      fos.close(); 

     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     if(imageData != null){ 
      Intent mIntent = new Intent(); 
      setResult(0,mIntent); 
      PictureActivity.this.showDialog(0); 
     } 
    } 
}; 

provato anche a scrivere l'immagine da una Bitmap (non ha funzionato) , più un altro domanda qui la reportistica utilizzando un FileOutputStream lavorato

risposta

10

Trovato il problema:

Guardando le immagini originali f mostra la SDCARD:

  1. Le immagini contengono dati GPS EXIF ​​se si utilizza EXIFInterface. I dati GPS, tuttavia, erano sbagliati (vedi sotto) - che probabilmente è il motivo per cui Flickr non lo mostrerà.

  2. Utilizzando il metodo che imposta le coordinate GPS tramite i parametri della videocamera NON scrivere i dati GPS EXIF ​​(questo stava utilizzando coordinate dummy codificate, non ho provato con una correzione GPS effettiva). Non ho cercato di capire perché è così.

L'API di Android per EXIFInterface ha this documentation:

public static final String TAG_GPS_LONGITUDE
dal: Livello API 5
String. Il formato è "num1/denom1, num2/denom2, num3/denom3".
Costante Valore: "GPSLongitude"

Il problema con il mio codice originale è che stavo passando le coordinate GPS in gradi decimali - le coordinate si ottiene da chiamare getLatitude/getLogitude su un oggetto posizione è in gradi decimali . EXIFInterface si aspetta le coordinate in gradi minuti secondi e quindi scritte come razionali (questo fa parte delle specifiche EXIF). Altro su formati di coordinate GPS e conversione here.

-è un'altra domanda/risposta che spiega come convertire da gradi decimali a Gradi minuti secondi.

Utilizzando questo codice, le coordinate GPS viene scritto correttamente nel EXIF ​​e Flickr hanno alcun problema importando i dati:

ExifInterface exif; 

double latitude = AGlanceLocationListener.getLatitude(); 
double longitude = AGlanceLocationListener.getLongitude(); 

try { 
    exif = new ExifInterface("/sdcard/DCIM/"+filename+".jpeg"); 
    int num1Lat = (int)Math.floor(latitude); 
    int num2Lat = (int)Math.floor((latitude - num1Lat) * 60); 
    double num3Lat = (latitude - ((double)num1Lat+((double)num2Lat/60))) * 3600000; 

    int num1Lon = (int)Math.floor(longitude); 
    int num2Lon = (int)Math.floor((longitude - num1Lon) * 60); 
    double num3Lon = (longitude - ((double)num1Lon+((double)num2Lon/60))) * 3600000; 

    exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, num1Lat+"/1,"+num2Lat+"/1,"+num3Lat+"/1000"); 
    exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, num1Lon+"/1,"+num2Lon+"/1,"+num3Lon+"/1000"); 


    if (latitude > 0) { 
     exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "N"); 
    } else { 
     exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "S"); 
    } 

    if (longitude > 0) { 
     exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "E");  
    } else { 
    exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "W"); 
    } 

    exif.saveAttributes(); 

} catch (IOException e) { 
    Log.e("PictureActivity", e.getLocalizedMessage()); 
} 

Nota: Quando si utilizza Gradi Minuti Secondi anche è necessario impostare gli attributi di riferimento GPS (N, S, E, W).

+1

Per latitudini e longitudini negativi il codice sopra codificherà valori negativi invece di quelli positivi. Ad esempio (-40,00, -73,00) sarà codificato come -40 gradi di latitudine sud invece di +40 gradi sud. -73 sarà codificato come -73 gradi di longitudine ovest invece di +73 gradi di longitudine ovest). – Theo

+1

Generalmente non dovrebbe essere considerata la risposta corretta, dal momento che il codice è difettoso (problema matematico, ecc ...) – seanpj

12

Sfortunatamente, questo funziona solo su un quarto dell'emisfero. A est di Greenwich ea nord dell'equatore. È così che ho immaginato che tu dovessi vivere lì :). Il tuo 'Math.floor' renderà errati tutti i valori negativi (come -105 in -106). Ecco la stessa cosa, che dovrebbe funzionare anche negli Stati Uniti.

public void loc2Exif(String flNm, Location loc) { 
    try { 
    ExifInterface ef = new ExifInterface(flNm); 
    ef.setAttribute(ExifInterface.TAG_GPS_LATITUDE, dec2DMS(loc.getLatitude())); 
    ef.setAttribute(ExifInterface.TAG_GPS_LONGITUDE,dec2DMS(loc.getLongitude())); 
    if (loc.getLatitude() > 0) 
     ef.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "N"); 
    else    
     ef.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "S"); 
    if (loc.getLongitude()>0) 
     ef.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "E");  
    else    
     ef.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "W"); 
    ef.saveAttributes(); 
    } catch (IOException e) {}   
} 
//----------------------------------------------------------------------------------- 
String dec2DMS(double coord) { 
    coord = coord > 0 ? coord : -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) * 60000;    // .259258 * 60000 = 15555 
    sOut = sOut + Integer.toString((int)coord) + "/1000"; // 105/1,59/1,15555/1000 
    return sOut; 
} 

... e una volta che mi ha iniziato, ecco il contrario

public Location exif2Loc(String flNm) { 
    String sLat = "", sLatR = "", sLon = "", sLonR = ""; 
    try { 
    ExifInterface ef = new ExifInterface(flNm); 
    sLat = ef.getAttribute(ExifInterface.TAG_GPS_LATITUDE); 
    sLon = ef.getAttribute(ExifInterface.TAG_GPS_LONGITUDE); 
    sLatR = ef.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF); 
    sLonR = ef.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF); 
    } catch (IOException e) {return null;} 

    double lat = dms2Dbl(sLat); 
    if (lat > 180.0) return null; 
    double lon = dms2Dbl(sLon); 
    if (lon > 180.0) return null; 

    lat = sLatR.contains("S") ? -lat : lat; 
    lon = sLonR.contains("W") ? -lon : lon; 

    Location loc = new Location("exif"); 
    loc.setLatitude(lat); 
    loc.setLongitude(lon); 
    return loc; 
} 
//------------------------------------------------------------------------- 
double dms2Dbl(String sDMS){ 
    double dRV = 999.0; 
    try { 
    String[] DMSs = sDMS.split(",", 3); 
    String s[] = DMSs[0].split("/", 2); 
    dRV = (new Double(s[0])/new Double(s[1])); 
    s = DMSs[1].split("/", 2); 
    dRV += ((new Double(s[0])/new Double(s[1]))/60); 
    s = DMSs[2].split("/", 2); 
    dRV += ((new Double(s[0])/new Double(s[1]))/3600); 
    } catch (Exception e) {} 
    return dRV; 
} 

... e un giorno, inizierò a scrivere piuttosto guardare il codice. geotagging felice, Sean

3

Questa soluzione soddisfare valori lng/lat negativi e positivi:

static public boolean setGeoTag(File image, LatLng geoTag) { 
    if (geoTag != null) { 
     try { 
      ExifInterface exif = new ExifInterface(
        image.getAbsolutePath()); 

      double latitude = Math.abs(geoTag.latitude); 
      double longitude = Math.abs(geoTag.longitude); 

      int num1Lat = (int) Math.floor(latitude); 
      int num2Lat = (int) Math.floor((latitude - num1Lat) * 60); 
      double num3Lat = (latitude - ((double) num1Lat + ((double) num2Lat/60))) * 3600000; 

      int num1Lon = (int) Math.floor(longitude); 
      int num2Lon = (int) Math.floor((longitude - num1Lon) * 60); 
      double num3Lon = (longitude - ((double) num1Lon + ((double) num2Lon/60))) * 3600000; 

      String lat = num1Lat + "/1," + num2Lat + "/1," + num3Lat + "/1000"; 
      String lon = num1Lon + "/1," + num2Lon + "/1," + num3Lon + "/1000"; 

      if (geoTag.latitude > 0) { 
       exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "N"); 
      } else { 
       exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, "S"); 
      } 

      if (geoTag.longitude > 0) { 
       exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "E"); 
      } else { 
       exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, "W"); 
      } 

      exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, lat); 
      exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, lon); 

      exif.saveAttributes(); 

     } catch (IOException e) { 
      e.printStackTrace(); 
      return false; 
     } 
    } else { 
     return false; 
    } 
    return true; 
} 
Problemi correlati