如何使用C#为JPG图片写入经纬度元数据?(附问题代码)
修正JPG元数据写入经纬度的代码
你的代码存在几个核心问题:
- 复用同一个
PropertyItem对象多次修改,易引发元数据写入异常 - 经纬度存储格式不符合EXIF规范:EXIF的GPS经纬度并非直接存储double值,而是以度分秒的有理数组(每个分量用分子/分母整数对表示)存储,且需配套方向标签(南北纬/东西经)
以下是修正后的完整代码:
using System.Drawing.Imaging; using System.IO; using System.Text; using System.Reflection; public static class BitmapExtensions { public static Bitmap WriteCoordinatesToImage(this Bitmap sourceBitmap, double latitude, double longitude) { // 处理纬度:转换为度分秒格式,添加南北纬标签 var latDms = ConvertDegreesToDms(latitude); AddGpsProperty(sourceBitmap, 0x0001, 2, Encoding.ASCII.GetBytes((latitude >= 0 ? "N" : "S") + '\0')); AddGpsProperty(sourceBitmap, 0x0002, 5, GetDmsBytes(latDms)); // 处理经度:转换为度分秒格式,添加东西经标签 var lonDms = ConvertDegreesToDms(longitude); AddGpsProperty(sourceBitmap, 0x0003, 2, Encoding.ASCII.GetBytes((longitude >= 0 ? "E" : "W") + '\0')); AddGpsProperty(sourceBitmap, 0x0004, 5, GetDmsBytes(lonDms)); return sourceBitmap; } // 将小数度数转为度分秒 private static (int Degrees, int Minutes, double Seconds) ConvertDegreesToDms(double degrees) { var absDeg = Math.Abs(degrees); int d = (int)absDeg; double rem = (absDeg - d) * 60; int m = (int)rem; double s = (rem - m) * 60; return (d, m, s); } // 将度分秒转为EXIF要求的有理数组字节 private static byte[] GetDmsBytes((int Degrees, int Minutes, double Seconds) dms) { using var ms = new MemoryStream(); using var bw = new BinaryWriter(ms); // 度:分子=度数,分母=1 bw.Write(dms.Degrees); bw.Write(1); // 分:分子=分数,分母=1 bw.Write(dms.Minutes); bw.Write(1); // 秒:用整数分子分母避免精度损失 int secNum = (int)(dms.Seconds * 10000); bw.Write(secNum); bw.Write(10000); return ms.ToArray(); } // 新增GPS属性项(解决PropertyItem无法直接实例化的问题) private static void AddGpsProperty(Bitmap bitmap, int propId, short propType, byte[] value) { PropertyItem prop; // 反射创建PropertyItem实例(因为无公开构造函数) var ctor = typeof(PropertyItem).GetConstructor( BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null); prop = (PropertyItem)ctor.Invoke(null); prop.Id = propId; prop.Type = propType; prop.Len = value.Length; prop.Value = value; bitmap.SetPropertyItem(prop); } }
重要说明:
- EXIF GPS标签规范:
GPSLatitude(0x0002)和GPSLongitude(0x0004)的类型为5(有理类型),长度固定为24字节(3个度分秒分量×8字节/分量)GPSLatitudeRef(0x0001)、GPSLongitudeRef(0x0003)为ASCII字符串类型(类型2),存储"N"/"S"或"E"/"W",必须以\0结尾
- 由于
PropertyItem无公开构造函数,通过反射创建新实例,避免复用现有属性导致的冲突
内容的提问来源于stack exchange,提问作者user19626293
相关产品推荐
相关产品推荐

