经纬度相关业务及核心算法

经纬度经典用处

最近在公司做了一个地图的项目,核心的算法就是经纬度计算距离根据距离扩展经纬度
闲话少说,直接贴出代码看注释吧

经纬度计算距离

    // 地球半径
    private static double EARTH_RADIUS = 6378.137;

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    /**
     * 通过经纬度获取距离(单位:米)
     *
     * @param lat1
     * @param lng1
     * @param lat2
     * @param lng2
     * @return 距离
     */
    public static double getDistance(double lat1, double lng1, double lat2,
                                     double lng2) {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(radLat1) * Math.cos(radLat2)
                * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000d) / 10000d;
        s = s * 1000;
        return s;
    }

根据距离扩展经纬度


    public static final double Ea = 6378137;     //   赤道半径
    public static final double Eb = 6356725;     //   极半径

    /***
     *  这个根据一个经纬度坐标、距离然后求另外一个经纬度坐标的函数
     *  例如,我要找一个坐标点(lat,lon)的5公里范围内的所有商户信息、景点信息等。这个MBR就是一个最大的范围,
     *  这个矩形是包含5公里范围内所有这些有效信息的一个最小矩形。
     *  求出四个方向0度、90度、180度、270度方向上的四个坐标点就可以得到这个矩形。
     * @param lat
     * @param lon
     * @param distance 距离,单位:公里
     * @param angle 正北极方向顺时针的角度
     * @return
     */
    public static LatLon getLatLon(double lat, double lon, double distance, double angle){
        double dx = distance * 1000 * Math.sin(angle * Math.PI / 180.0);
        double dy = distance * 1000 * Math.cos(angle * Math.PI / 180.0);
        double ec = Eb + (Ea-Eb) * (90.0 - lat) / 90.0;
        double ed = ec * Math.cos(lat * Math.PI / 180);
        double newLon = (dx / ed + lon * Math.PI / 180.0) * 180.0 / Math.PI;
        double newLat = (dy / ec + lat * Math.PI / 180.0) * 180.0 / Math.PI;
        return new LatLon(newLat,newLon);
    }

    public static class LatLon{
        double lat;
        double lon;

        public LatLon(double lat, double lon) {
            this.lat = lat;
            this.lon = lon;
        }

        public double getLat() {
            return lat;
        }

        public void setLat(double lat) {
            this.lat = lat;
        }

        public double getLon() {
            return lon;
        }

        public void setLon(double lon) {
            this.lon = lon;
        }

        public String toString(){
            return "lat:"+lat+",lon:"+lon;
        }
    }

测试一把

public static void main(String[] args){
        double lat = 32.402130126953125;
        double lon = 119.42626953125;
        System.out.println("left:"+getLatLon(lat,lon,4,0));
        System.out.println("up:"+getLatLon(lat,lon,4,90));
        System.out.println("right:"+getLatLon(lat,lon,4,180));
        System.out.println("down:"+getLatLon(lat,lon,4,270));
    }

运行结果:

left:lat:32.438106220211,lon:119.42626953125003
up:lat:32.402130126953125,lon:119.46887968427875
right:lat:32.36615403369525,lon:119.42626953125003
down:lat:32.402130126953125,lon:119.38365937822127

Process finished with exit code 0
<think>嗯,用户问的是C#如何将经纬度转换为瓦片序号。首先,我需要回想一下瓦片地图的工作原理。通常,像Google地图或OpenStreetMap这样的在线地图服务使用瓦片系统来分割地图,每个瓦片都有唯一的x、y、z坐标,其中z代表缩放级别。经纬度转瓦片坐标的算法是关键。 首先,用户可能需要了解转换的基本公式。我记得将经纬度转换为瓦片坐标需要几个步骤:将经度转换为x,纬度转换为y,然后根据缩放级别计算具体的瓦片编号。可能需要使用墨卡托投影,或者更具体地说,Web墨卡托投影(EPSG:3857),这是大多数在线地图服务使用的投影方式。 接着,我应该考虑具体的数学公式。经度转x瓦片的公式大致是:x = (经度 + 180) / 360 * 2^z,然后取整数部分。纬度的话稍微复杂一点,因为需要应用墨卡托投影的转换公式,将纬度转换为y坐标。公式可能是这样的:y = (1 - log(tan(纬度*π/180) + 1/cos(纬度*π/180)) / π) / 2 * 2^z,然后同样取整数部分。 不过,可能需要注意纬度的范围,因为墨卡托投影在极区会有问题,所以纬度不能超过85.0511度。这个细节需要提醒用户,避免转换时出现错误。 接下来,我需要用C#代码来实现这个公式。首先,将经度转换为x瓦片编号相对直接,但纬度部分需要使用Math类的函数,比如Math.Log和Math.Tan。需要注意的是C#中的三角函数使用的是弧度,所以要将纬度从度数转换为弧度。 另外,用户可能需要知道瓦片的坐标系是否是从左上角开始,这在某些地图服务中是标准的。例如,在z=0时,整个地图被一个瓦片覆盖,x=0,y=0。随着缩放级别的增加,瓦片数量呈指数增长。 可能的代码结构是写一个静态方法,输入经纬度和缩放级别,返回x和y的瓦片编号。需要注意的是,在计算纬度时,可能会有一些边界情况需要处理,比如当纬度接近极地时,或者缩放级别过大导致瓦片编号超出范围的情况。 此外,可能需要考虑不同地图服务的瓦片坐标原点是否不同。例如,有的地图服务可能使用TMS规范,y坐标的原点在底部,而OSM可能在顶部,这时候需要调整y的计算公式。但通常情况下,Web墨卡托投影的瓦片坐标原点在左上角,所以上述公式适用。 测试例子也是重要的,比如用户给出的示例,比如经度116.3883,纬度39.9283,缩放级别10,正确的瓦片编号应该是x=591,y=270。我需要确保代码能够正确计算这个例子,并且处理不同的输入。 可能还需要提醒用户,不同的地图服务可能有不同的瓦片编号方案,比如是否使用反转的y轴,或者是否有不同的缩放级别偏移。因此,在实际应用中需要根据具体的地图服务提供商调整公式。 总结一下,我需要提供一个清晰的步骤解释,包括公式、代码实现、注意事项以及示例。确保用户能够理解每一步的原理,并且能够将代码应用到实际项目中。同时,提醒可能出现的常见问题,如纬度限制、坐标系差异等,帮助用户避免错误。</think>在在线地图服务(如Google Maps、OpenStreetMap)中,**经纬度坐标转换为瓦片序号**是加载地图瓦片的关键步骤。以下是使用C#实现的原理、公式及示例代码: --- ### **核心公式** 瓦片坐标系基于 **Web墨卡托投影(EPSG:3857)**,公式如下: 1. **经度 → X轴瓦片号** \[ x = \left\lfloor \frac{lon + 180}{360} \times 2^z \right\rfloor \] 2. **纬度 → Y轴瓦片号** \[ y = \left\lfloor \left(1 - \frac{\ln\left(\tan\left(lat \times \frac{\pi}{180}\right) + \sec\left(lat \times \frac{\pi}{180}\right)\right)}{\pi}\right) \times 2^{z-1} \right\rfloor \] - `lon`: 经度(-180到180) - `lat`: 纬度(-85.0511到85.0511,因墨卡托投影限制) - `z`: 缩放级别(0到20+,视地图服务而定) - `floor()`: 向下取整 --- ### **C#代码实现** ```csharp using System; public class TileSystem { /// <summary> /// 将经纬度转换为瓦片坐标 /// </summary> public static (int x, int y) LatLonToTile(double lat, double lon, int zoom) { // 计算X轴瓦片号 double xTile = (lon + 180.0) / 360.0 * Math.Pow(2, zoom); int x = (int)Math.Floor(xTile); // 计算Y轴瓦片号 double latRad = lat * Math.PI / 180.0; double yTile = (1 - Math.Log(Math.Tan(latRad) + 1 / Math.Cos(latRad)) / Math.PI) / 2 * Math.Pow(2, zoom); int y = (int)Math.Floor(yTile); return (x, y); } /// <summary> /// 示例:北京坐标转瓦片号(缩放级别10) /// </summary> public static void Main() { double lat = 39.9283; // 纬度 double lon = 116.3883; // 经度 int zoom = 10; var (x, y) = LatLonToTile(lat, lon, zoom); Console.WriteLine($"瓦片坐标: x={x}, y={y}"); // 输出: x=591, y=270 } } ``` --- ### **关键注意事项** 1. **纬度范围限制** 纬度必须介于 `-85.0511°` 到 `85.0511°`,超出范围会导致计算错误(因墨卡托投影无法覆盖极地)。 2. **瓦片坐标系原点** 瓦片坐标通常以左上角为原点(如OpenStreetMap),但部分地图服务可能使用左下角(如TMS标准),此时需反转Y轴: ```csharp y = (int)(Math.Pow(2, zoom) - 1) - y; ``` 3. **缩放级别范围** 常见缩放级别为0(全球1瓦片)到20+(街道级细节),具体上限取决于地图服务。 --- ### **应用场景** - 动态加载地图瓦片(如生成静态地图、离线地图缓存) - 空间数据可视化(将业务数据与瓦片坐标对齐) - 路径规划或地理围栏的瓦片索引计算 如果需要进一步处理瓦片URL(如拼接OpenStreetMap瓦片),可参考格式: ```csharp string tileUrl = $"https://tile.openstreetmap.org/{zoom}/{x}/{y}.png"; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值