GIS开发-如何实现根据geometry裁切栅格地图瓦片服务

22 阅读1分钟
// 将经纬度映射到图片像素坐标(线性映射,假设图像是 Plate Carrée / 等经纬投影)
function lonLatToPixel(lon: number, lat: number, bounds: any, width: number, height: number) {
    const { west, south, east, north } = bounds
    const x = ((lon - west) / (east - west)) * width
    const y = ((north - lat) / (north - south)) * height // canvas y 向下,所以用 north - lat
    return [x, y]
}
// 把 GeoJSON 多边形渲染为画布上的路径并用 globalCompositeOperation 做裁剪
async function maskImageWithGeoJSON(imageUrl: string, geojson: any, imageBounds: any) {
    const img: any = await loadImage(imageUrl)
    const width = img.width
    const height = img.height

    const canvas = document.createElement('canvas')
    canvas.width = width
    canvas.height = height
    const ctx: any = canvas.getContext('2d')

    // 先把原图画上去
    ctx.drawImage(img, 0, 0, width, height)

    // 准备路径
    ctx.beginPath()

    const addRing = (ring: any) => {
        if (!ring || ring.length === 0) return
        const [x0, y0] = lonLatToPixel(ring[0][0], ring[0][1], imageBounds, width, height)
        ctx.moveTo(x0, y0)
        for (let i = 1; i < ring.length; i++) {
            const [x, y] = lonLatToPixel(ring[i][0], ring[i][1], imageBounds, width, height)
            ctx.lineTo(x, y)
        }
        ctx.closePath()
    }

    const processGeometry = (geom: any) => {
        if (!geom) return
        if (geom.type === 'Polygon') {
            for (const ring of geom.coordinates) addRing(ring)
        } else if (geom.type === 'MultiPolygon') {
            for (const poly of geom.coordinates) {
                for (const ring of poly) addRing(ring)
            }
        } else {
            console.warn('不支持的几何类型:', geom.type)
        }
    }

    if (geojson.type === 'FeatureCollection') {
        for (const feature of geojson.features) processGeometry(feature.geometry)
    } else if (geojson.type === 'Feature') {
        processGeometry(geojson.geometry)
    } else {
        processGeometry(geojson)
    }

    // 使用 destination-in:保留在路径内的图像像素,其余变为透明
    ctx.globalCompositeOperation = 'destination-in'
    ctx.fillStyle = 'black'
    // 使用 evenodd 支持带孔的多边形
    ctx.fill('evenodd')
    return canvas.toDataURL('image/png')
}