Flutter中OpenStreetMap经纬度定位的ImageOverlay旋转实现
解决flutter_map中单个OverlayImage旋转问题
核心思路
flutter_map不支持单独旋转单个OverlayImage,只能通过定义旋转后的边界框三个顶点经纬度实现单个图片的旋转效果——利用OverlayImage的bounds参数(需传入NW、SE、SW三个经纬度点),通过几何计算得到旋转后的三点坐标。
具体实现步骤
1. 计算初始未旋转状态的三个顶点
已知NW(西北)和SE(东南)经纬度,先算出SW(西南)点:
- SW的纬度 = SE的纬度(同南边界)
- SW的经度 = NW的经度(同西边界)
2. 计算图片的中心经纬度
中心经纬度是NW和SE的中点:
LatLng calculateCenter(LatLng nw, LatLng se) { return LatLng( (nw.latitude + se.latitude) / 2, (nw.longitude + se.longitude) / 2, ); }
3. 经纬度与平面坐标互转(用于旋转计算)
经纬度是球面坐标,旋转计算需转成平面投影坐标(如墨卡托投影),可借助flutter_map自带的Crs完成转换:
// 获取当前地图的CRS(示例为EPSG3857) final crs = mapController.crs; // 经纬度转平面坐标(单位:米) Point<double> latLngToPoint(LatLng latLng) { return crs.latLngToPoint(latLng, mapController.zoom); } // 平面坐标转回经纬度 LatLng pointToLatLng(Point<double> point) { return crs.pointToLatLng(point, mapController.zoom); }
4. 绕中心执行旋转变换
使用二维旋转公式,将每个点绕中心平面坐标旋转指定角度(弧度):
// 角度转弧度 double degreesToRadians(double degrees) => degrees * pi / 180; Point<double> rotatePoint(Point<double> center, Point<double> point, double angleRadians) { final dx = point.x - center.x; final dy = point.y - center.y; final rotatedX = center.x + dx * cos(angleRadians) - dy * sin(angleRadians); final rotatedY = center.y + dx * sin(angleRadians) + dy * cos(angleRadians); return Point(rotatedX, rotatedY); }
5. 生成旋转后的OverlayImage
将旋转后的平面坐标转回经纬度,传入OverlayImage的bounds参数:
// 初始的NW、SE点(替换为你的实际坐标) final LatLng originalNW = LatLng(39.9042, 116.4074); final LatLng originalSE = LatLng(39.9000, 116.4100); // 计算初始SW点 final LatLng originalSW = LatLng(originalSE.latitude, originalNW.longitude); // 旋转角度(示例为45度) final double rotateAngle = 45; // 计算中心经纬度及平面坐标 final LatLng center = calculateCenter(originalNW, originalSE); final Point<double> centerPoint = latLngToPoint(center); final Point<double> nwPoint = latLngToPoint(originalNW); final Point<double> sePoint = latLngToPoint(originalSE); final Point<double> swPoint = latLngToPoint(originalSW); // 执行旋转计算 final double angleRad = degreesToRadians(rotateAngle); final Point<double> rotatedNWPoint = rotatePoint(centerPoint, nwPoint, angleRad); final Point<double> rotatedSEPoint = rotatePoint(centerPoint, sePoint, angleRad); final Point<double> rotatedSWPoint = rotatePoint(centerPoint, swPoint, angleRad); // 转回经纬度 final LatLng rotatedNW = pointToLatLng(rotatedNWPoint); final LatLng rotatedSE = pointToLatLng(rotatedSEPoint); final LatLng rotatedSW = pointToLatLng(rotatedSWPoint); // 创建旋转后的OverlayImage final OverlayImage rotatedImage = OverlayImage( bounds: LatLngBounds.fromPoints([rotatedNW, rotatedSE, rotatedSW]), imageProvider: AssetImage('assets/your_image.png'), );
注意事项
- 旋转计算依赖当前地图缩放级别,若缩放变化,需在
mapController.onZoomChanged回调中重新计算并更新OverlayImage - 小范围区域内使用墨卡托投影的平面旋转误差可忽略,若需大区域精确旋转,可改用基于地球半径的球面旋转算法
内容的提问来源于stack exchange,提问作者rev_dev_01
相关产品推荐
相关产品推荐

