mapbox + postgis + c# 构建一个通用的矢量数据地图
写在前面
- 您可以使用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/geobuf@3.0.2/dist/geobuf.js"></script>
<script src=" https://unpkg.com/pbf@3.0.5/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
狗子代表了我此刻的心情😄
end
本示例仅仅是对postgis api 的简单封装,如果在项目中对空间数据仅做json转换,推荐使用 NetTopologySuite.IO.GeoJson这个包,简单好用,如果想在swagger中以geojson或wkt方式显示NetTopologySuite.Geometry 中Geometry类(包扩子类),可以在上面的仓库中找到NetTopologySuite的swagger扩展。
最后欢迎start 谢谢🙏