原理
手机拍照时,如果开启位置信息,那么,拍出来的照片将会有经纬度信息。获取到这个经纬度信息后,可以查询到拍照时的位置信息。
以上为实现原理。
实现
依赖导入
从博文上看是exifread模块,找我大java的对应的jar,发现metadata-extractor,而且官方还在持续更新,最近的jar是今年的。
这个元数据提取jar非常强大,还支持视频信息的提取,看看官方介绍:
1 2 3 4 5
| <dependency> <groupId>com.drewnoakes</groupId> <artifactId>metadata-extractor</artifactId> <version>2.16.0</version> </dependency>
|
示例demo
这里先演示这个元数据提取jar能提取到的信息,顺便把取到的经纬度通过百度转地址。
因为是demo,没有业务,我这里就直接在测试类里干了。没有什么业务,不涉及什么机密,可以上全码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
| package com.easylinkin.bm.extractor;
import com.alibaba.fastjson.JSONObject; import com.drew.imaging.ImageMetadataReader; import com.drew.imaging.ImageProcessingException; import com.drew.metadata.Directory; import com.drew.metadata.Metadata; import com.drew.metadata.Tag; import com.easylinkin.bm.util.HttpUtils; import lombok.extern.slf4j.Slf4j;
import java.io.File; import java.io.IOException;
@Slf4j public class ImgTestCode { public static void main(String[] args) throws Exception {
File file = new File("d:\\IMG_test.jpg"); readImageInfo(file); }
private static void readImageInfo(File file) throws ImageProcessingException, Exception { Metadata metadata = ImageMetadataReader.readMetadata(file);
System.out.println("---打印全部详情---"); for (Directory directory : metadata.getDirectories()) { for (Tag tag : directory.getTags()) { System.out.format("[%s] - %s = %s\n", directory.getName(), tag.getTagName(), tag.getDescription()); } if (directory.hasErrors()) { for (String error : directory.getErrors()) { System.err.format("ERROR: %s", error); } } }
System.out.println("--打印常用信息---"); Double lat = null; Double lng = null; for (Directory directory : metadata.getDirectories()) { for (Tag tag : directory.getTags()) { String tagName = tag.getTagName(); String desc = tag.getDescription(); if (tagName.equals("Image Height")) { System.err.println("图片高度: " + desc); } else if (tagName.equals("Image Width")) { System.err.println("图片宽度: " + desc); } else if (tagName.equals("Date/Time Original")) { System.err.println("拍摄时间: " + desc); } else if (tagName.equals("GPS Latitude")) { System.err.println("纬度 : " + desc); System.err.println("纬度(度分秒格式) : " + pointToLatlong(desc)); lat = latLng2Decimal(desc); } else if (tagName.equals("GPS Longitude")) { System.err.println("经度: " + desc); System.err.println("经度(度分秒格式): " + pointToLatlong(desc)); lng = latLng2Decimal(desc); } } } System.err.println("--经纬度转地址--"); convertGpsToLoaction(lat, lng);
}
public static String pointToLatlong(String point) { Double du = Double.parseDouble(point.substring(0, point.indexOf("°")).trim()); Double fen = Double.parseDouble(point.substring(point.indexOf("°") + 1, point.indexOf("'")).trim()); Double miao = Double.parseDouble(point.substring(point.indexOf("'") + 1, point.indexOf("\"")).trim()); Double duStr = du + fen / 60 + miao / 60 / 60; return duStr.toString(); }
public static double latLng2Decimal(String gps) { String a = gps.split("°")[0].replace(" ", ""); String b = gps.split("°")[1].split("'")[0].replace(" ", ""); String c = gps.split("°")[1].split("'")[1].replace(" ", "").replace("\"", ""); double gps_dou = Double.parseDouble(a) + Double.parseDouble(b) / 60 + Double.parseDouble(c) / 60 / 60; return gps_dou; }
private static void convertGpsToLoaction(double gps_latitude, double gps_longitude) throws IOException { String apiKey = "YNxcSCAphFvuPD4LwcgWXwC3SEZZc7Ra";
String res = ""; String url = "http://api.map.baidu.com/reverse_geocoding/v3/?ak=" + apiKey + "&output=json&coordtype=wgs84ll&location=" + (gps_latitude + "," + gps_longitude); System.err.println("【url】" + url);
res = HttpUtils.httpGet(url); JSONObject object = JSONObject.parseObject(res); if (object.containsKey("result")) { JSONObject result = object.getJSONObject("result"); if (result.containsKey("addressComponent")) { JSONObject address = object.getJSONObject("result").getJSONObject("addressComponent"); System.err.println("拍摄地点:" + address.get("country") + " " + address.get("province") + " " + address.get("city") + " " + address.get("district") + " " + address.get("street") + " " + result.get("formatted_address") + " " + result.get("business")); } } }
}
|
控制台打印:
下面贴出详细内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| com.easylinkin.bm.extractor.ImgTestCode ---打印全部详情--- [JPEG] - Compression Type = Baseline [JPEG] - Data Precision = 8 bits [JPEG] - Image Height = 4032 pixels [JPEG] - Image Width = 3024 pixels [JPEG] - Number of Components = 3 [JPEG] - Component 1 = Y component: Quantization table 0, Sampling factors 2 horiz/2 vert [JPEG] - Component 2 = Cb component: Quantization table 1, Sampling factors 1 horiz/1 vert [JPEG] - Component 3 = Cr component: Quantization table 1, Sampling factors 1 horiz/1 vert [Exif IFD0] - Date/Time = 2021:08:20 09:39:58 [Exif IFD0] - Model = YOTA Y3 [Exif IFD0] - YCbCr Positioning = Center of pixel array [Exif IFD0] - Resolution Unit = Inch [Exif IFD0] - Y Resolution = 72 dots per inch [Exif IFD0] - X Resolution = 72 dots per inch [Exif IFD0] - Make = YOTA [GPS] - GPS Date Stamp = 2021:08:20 [GPS] - GPS Altitude Ref = Below sea level [GPS] - GPS Longitude Ref = E [GPS] - GPS Longitude = 114° 24' 9.61" [GPS] - GPS Processing Method = ASCII [GPS] - GPS Latitude Ref = N [GPS] - GPS Time-Stamp = 01:39:46.000 UTC [GPS] - GPS Altitude = 21 metres [GPS] - GPS Latitude = 30° 28' 40.67" [Exif SubIFD] - Color Space = sRGB [Exif SubIFD] - F-Number = f/1.9 [Exif SubIFD] - Date/Time Digitized = 2021:08:20 09:39:58 [Exif SubIFD] - Focal Length = 3.9 mm [Exif SubIFD] - Aperture Value = f/1.9 [Exif SubIFD] - Exposure Mode = Auto exposure [Exif SubIFD] - Sub-Sec Time Digitized = 819350 [Exif SubIFD] - Exif Image Height = 4032 pixels [Exif SubIFD] - Focal Length 35 = 23 mm [Exif SubIFD] - Scene Capture Type = Standard [Exif SubIFD] - Sub-Sec Time Original = 819350 [Exif SubIFD] - Exposure Program = Unknown (0) [Exif SubIFD] - White Balance Mode = Auto white balance [Exif SubIFD] - Exif Image Width = 3024 pixels [Exif SubIFD] - Sub-Sec Time = 819350 [Exif SubIFD] - Shutter Speed Value = 1/1022 sec [Exif SubIFD] - Metering Mode = Center weighted average [Exif SubIFD] - Date/Time Original = 2021:08:20 09:39:58 [Exif SubIFD] - Components Configuration = YCbCr [Exif SubIFD] - Exif Version = 2.20 [Exif SubIFD] - Flash = Flash did not fire [Exif SubIFD] - Brightness Value = 0.0 [Exif SubIFD] - ISO Speed Ratings = 103 [Exif SubIFD] - Sensing Method = One-chip color area sensor [Exif SubIFD] - FlashPix Version = 1.00 [Exif SubIFD] - Exposure Time = 1/1023 sec [Interoperability] - Interoperability Index = Recommended Exif Interoperability Rules (ExifR98) [Interoperability] - Interoperability Version = 1.00 [Exif Thumbnail] - Y Resolution = 72 dots per inch [Exif Thumbnail] - Thumbnail Length = 21538 bytes [Exif Thumbnail] - Thumbnail Offset = 959 bytes [Exif Thumbnail] - Compression = JPEG (old-style) [Exif Thumbnail] - Resolution Unit = Inch [Exif Thumbnail] - X Resolution = 72 dots per inch [Huffman] - Number of Tables = 4 Huffman tables [File Type] - Detected File Type Name = JPEG [File Type] - Detected File Type Long Name = Joint Photographic Experts Group [File Type] - Detected MIME Type = image/jpeg [File Type] - Expected File Name Extension = jpg [File] - File Name = IMG_20210820_093958.jpg [File] - File Size = 5215044 bytes [File] - File Modified Date = 星期五 八月 20 09:39:59 +08:00 2021 --打印常用信息--- 初始化HttpClientTest~~~开始 图片高度: 4032 pixels 图片宽度: 3024 pixels 经度: 114° 24' 9.61" 经度(度分秒格式): 114.40266944444446 纬度 : 30° 28' 40.67" 纬度(度分秒格式) : 30.477963888888887 拍摄时间: 2021:08:20 09:39:58 --经纬度转地址-- 【url】http://api.map.baidu.com/reverse_geocoding/v3/?ak=YNxcSCAphFvuPD4LwcgWXwC3SEZZc7Ra&output=json&coordtype=wgs84ll&location=30.477963888888887,114.40266944444446 初始化HttpClientTest~~~结束 拍摄地点:中国 湖北省 武汉市 洪山区 软件园路 湖北省武汉市洪山区软件园路9 关山,光谷天地
|
上面的提取到的内容我就不解释了,应该看得懂,不懂的,可以翻译英文,或者查API看打印的是啥。其他文件我就不演示了,有兴趣的可以自己试试。我的百度地图的AK就先放这里,方便大家验证,免得说我骗人,反正我也是免费用的。最后再说一句,图片发送要么压缩到压缩包再发送,要么用数据线从手机里拷出来。我这里先用微信发的,基本上信息都被抹除了(在电脑上查看图片详情,其实也可以看到经纬度信息的)。还有,我还有个苹果手机,其实也是可以拍有地理位置信息的照片的,要打开隐私里的定位,授权照相机。
总结与衍生想法
这个怎么说呢,还是很不错的。用到我们的工作中的话,我们觉得可以替代我们之前做的一个打点巡检的,到达巡检位置拍张照片再配合机器码,不怕你让别人代拍照片了。还有考勤的公出单、外勤等等。
另外还想到这出门在外爱拍照的娃们,你们的照片放到云存储上,然后如果有无良服务商,基本可以把你的轨迹通过你上传的照片时间绘制出来。。。
好了,这个就分享到这里。这里其实还给我一个最大的感受就是,如果我不知道A就不会想到B。要是我早知道图片可以携带的信息,或者知道照相机软件可以获取的信息,可能可以针对这些早点做点什么。。。。。。