天天看点

mapbox + postgis + c# 构建一个通用的矢量数据地图

mapbox + postgis + net6 构建一个通用的矢量数据地图

写在前面

  • 您可以使用geoserver轻松的发布一个基于postgis的矢量地图,我只是懒,不想安装 😂 (虽然你可以使用docker轻松安装)
  • 关于语言方面的您可以使用任意一款您喜欢的, 我就喜欢C#
  • 仓库地址 :dotgeo-extensions

代码

webpi

代码很简单,全是sql。包括 mvt 、geobuf 、geojson的查询

public async Task<byte[]> GetMvtBufferAsync(
        string connectionString, 
        string table, 
        string geomColumn, 
        int z, 
        int x,
        int y, 
        string? columns,
        string? filter)
    {
        connectionString.ThrowIfNullOrWhiteSpace(nameof(connectionString));
        table.ThrowIfNullOrWhiteSpace(nameof(table));
        geomColumn.ThrowIfNullOrWhiteSpace(nameof(geomColumn));
        
        var sql = $@"
            WITH mvt_geom as (
              SELECT
                ST_AsMVTGeom (
                  ST_Transform({geomColumn}, 3857),
                  ST_TileEnvelope({z}, {x}, {y})
                ) as geom
                {(columns != null ? $",{columns}" : "")}
              FROM
                {table},
                (SELECT ST_SRID({geomColumn}) AS srid FROM {table} LIMIT 1) a
              WHERE
                ST_Intersects(
                  {geomColumn},
                  ST_Transform(ST_TileEnvelope({z}, {x}, {y}),srid)
                ) {(filter != null ? $" AND {filter}" : "")}
            )
            SELECT ST_AsMVT(mvt_geom.*, '{table}', 4096, 'geom') AS mvt from mvt_geom;";

        return await QuerySingleValueAsync<byte[]>(connectionString, sql);
    }

    public async Task<byte[]> GetGeoBufferAsync(
        string connectionString, 
        string table, 
        string geomColumn,
        string? columns, 
        string? filter)
    {
        connectionString.ThrowIfNullOrWhiteSpace(nameof(connectionString));
        table.ThrowIfNullOrWhiteSpace(nameof(table));
        geomColumn.ThrowIfNullOrWhiteSpace(nameof(geomColumn));
        
        var sql = $@"SELECT ST_AsGeobuf(q, 'geom')
                          FROM (SELECT
                                  ST_Transform({geomColumn}, 4326) as geom
                                  {(columns != null ? $", {columns}" : "")}
                                FROM
                                  {table}
                                {(filter != null ? $"WHERE {filter}" : "")}
                          ) as q;";

        return await QuerySingleValueAsync<byte[]>(connectionString, sql);
    }

    public async Task<object> GetGeoJsonAsync(
        string connectionString, 
        string table, 
        string geomColumn, 
        string? idColumn,
        string? columns,
        string? filter)
    {
        connectionString.ThrowIfNullOrWhiteSpace(nameof(connectionString));
        table.ThrowIfNullOrWhiteSpace(nameof(table));
        geomColumn.ThrowIfNullOrWhiteSpace(nameof(geomColumn));
        
        var sql = $@"
            SELECT
                row_to_json(fc)
            FROM (
                SELECT
                    'FeatureCollection' AS type
                    ,array_to_json(array_agg(f)) AS features
                FROM (
                    SELECT
                        'feature' AS type
                        {(idColumn != null ? $",{idColumn} as id" : "")}
                        , ST_AsGeoJSON({geomColumn})::json as geometry  --geom表中的空间字段
                        , (
                            SELECT
                                row_to_json(t)
                            FROM (
                                SELECT
                                   {columns ?? ""}
                                ) AS t
                            ) AS properties
                    FROM {table} 
                    {(filter != null ? $"WHERE {filter}" : "")} ) AS f
               ) AS fc";

        return await QuerySingleValueAsync<object>(connectionString, sql);
    }

    private static async Task<T> QuerySingleValueAsync<T>(string connectionString, string sql, Array? parameters = null)
    {
        await using var conn = new NpgsqlConnection(connectionString);
        await conn.OpenAsync();
        await using var cmd = new NpgsqlCommand(sql, conn);

        if (parameters != null)
            cmd.Parameters.AddRange(parameters);

        await using var reader = await cmd.ExecuteReaderAsync();
        await reader.ReadAsync();

        return (T) reader[0];
    }

           

mapbox

加载 mvt 或 geobuf 或 geojson

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title></title>
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
    <link href="https://api.mapbox.com/mapbox-gl-js/v2.8.2/mapbox-gl.css" rel="stylesheet">
    <script src="https://api.mapbox.com/mapbox-gl-js/v2.8.2/mapbox-gl.js"></script>
    <script src="https://unpkg.com/[email protected]/dist/geobuf.js"></script>
    <script src=" https://unpkg.com/[email protected]/dist/pbf.js"></script>
    <script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-language/v1.0.0/mapbox-gl-language.js'></script>
    <style>
        body {
            margin: 0;
            padding: 0;
        }

        #map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }
    </style>
</head>

<body>
<div id="map"></div>
<script>
    function getQueryVariable(variable) {
        let query = window.location.search.substring(1);
        let vars = query.split("&");
        for (let i = 0; i < vars.length; i++) {
            let pair = vars[i].split("=");
            if (pair[0] === variable) {
                return pair[1];
            }
        }
        return false;
    }

    let layer_url = getQueryVariable("layer_url")
    let layer_type = getQueryVariable("layer_type")
    let layer_name = getQueryVariable("layer_name")
    
    document.title = layer_type;

    // TO MAKE THE MAP APPEAR YOU MUST    // ADD YOUR ACCESS TOKEN FROM
    // https://account.mapbox.com
    mapboxgl.accessToken =
        ''pk.eyJ1IjoiY29jYWluZWNvZGVyIiwiYSI6ImNrdHA1YjlleDBqYTEzMm85bTBrOWE0aXMifQ.J8k3R1QBqh3pyoZi_5Yx9w'';

    const map = new mapboxgl.Map({
        container: 'map',
        style: 'mapbox://styles/mapbox/light-v10',
        zoom: 10,
        center:[120.7,31]
    });

    map.on('load', () => {
        if (layer_type === "mvt"){
            map.addLayer({
                "id": "layer",
                type: 'fill',
                "source": {
                    "type": "vector",
                    "tiles": [
                        layer_url
                    ],
                    "minzoom": 6,
                },
                'source-layer': layer_name,
                'paint': {
                    'fill-color': '#fff000',
                    'fill-opacity': 0.3,
                    "fill-outline-color": "#000000",
                }
            })
        }
        else if(layer_type === "geobuf"){
            fetch(layer_url).then(res => {
                res.arrayBuffer().then(value => {
                    let geojson = geobuf.decode(new Pbf(value));
                    console.log(geojson)
                    map.addSource('test', {
                        'type': "geojson",
                        'data': geojson
                    });

                    map.addLayer({
                        'id': 'layer',
                        'type': 'fill',
                        'source': 'test',
                        'layout': {},
                        'paint': {
                            'fill-color': "red",
                            'fill-opacity': 0.3,
                            "fill-outline-color": "#000000",
                        }
                    });
                })
            }).catch(err => {
                console.error(err);
            })
        }else{
            fetch(layer_url).then(res => {
                res.json().then(value => {
                    map.addSource('test', {
                        'type': "geojson",
                        'data': JSON.parse(value)
                    });

                    map.addLayer({
                        'id': 'layer',
                        'type': 'fill',
                        'source': 'test',
                        'layout': {},
                        'paint': {
                            'fill-color': "#0000ff",
                            'fill-opacity': 0.3,
                            "fill-outline-color": "#0000ff",
                        }
                    });
                })
            })
        }
    });

    map.addControl(new mapboxgl.NavigationControl());
</script>

</body>

</html>

           

show

狗子代表了我此刻的心情😄

mapbox + postgis + c# 构建一个通用的矢量数据地图

end

本示例仅仅是对postgis api 的简单封装,如果在项目中对空间数据仅做json转换,推荐使用 NetTopologySuite.IO.GeoJson这个包,简单好用,如果想在swagger中以geojson或wkt方式显示NetTopologySuite.Geometry 中Geometry类(包扩子类),可以在上面的仓库中找到NetTopologySuite的swagger扩展。

最后欢迎start 谢谢🙏