使用 PostGIS 生成矢量图块

您喜欢视听学习吗?观看视频指南!
或者直接跳到代码
Overture Maps Foundation是由亚马逊、Meta、微软和 tomtom 发起的联合开发基金会项目,旨在创建可靠、易于使用、可互操作的开放地图数据。
Overture Maps 允许我们以GeoJSON格式下载开放地图数据(例如名胜古迹),我们可以将其转换为 SQL 并导入 Supabase 上的 Postgres 数据库。
使用 PostGIS,我们可以以编程方式生成矢量图块,并使用 supabase-js 将它们提供给我们的 MapLibre GL 客户端。
矢量图块是地理数据包,被打包成预定义的大致正方形的“图块”,以便在网络上传输。客户端请求的地图数据是一组“图块”,对应于预定义大小和位置的方形土地区域。
特别是对于大型数据集,这样做的好处是数据传输大大减少,因为只需要传输当前视口内和当前缩放级别的数据。
在本教程中,您将学习
- 使用 Overture Maps 以 GeoJSON 格式下载开放地图地点数据。
- 使用 GDAL ogr2ogr 将 GeoJSON 转换为 SQL 语句。
- 使用 psql 将位置数据和 JSON 元数据导入您的 Supabase Postgres 数据库。
- 使用 PostGIS
ST_AsMVT将与图块层对应的一组行聚合为二进制矢量图块表示。 addProtocol通过使用 supabase-js 进行远程过程调用,使用 MapLibre可视化大型 PostGIS 表。- 使用 supabase-js 按需获取其他 JSON 元数据
使用 Overture Maps 下载开放地图数据
Overture Maps 提供了一个Python 命令行工具来下载感兴趣区域内的数据并将其转换为几种常见的地理空间文件格式。
我们可以使用以下命令将新加坡的地点下载到 GeoJSON 文件中:
overturemaps download --bbox=103.570233,1.125077,104.115855,1.490957 -f geojson --type=place -o places.geojson
根据边界框的大小,这可能需要相当长的时间!
将 GeoJSON 转换为SQL
下一步,我们可以使用GDAL ogr2ogr将 GeoJSON 文件转换为 PostGIS 兼容的 SQL 文件。
您可以GDAL通过安装homebrew brew install gdal或按照下载说明进行操作。
PG_USE_COPY=true ogr2ogr -f pgdump places.sql places.geojson
将位置数据导入 Supabase
在专用的单独架构上启用 Supabase 数据库上的 PostGIS 扩展gis。为此,您可以导航到SQL 编辑器并运行以下 SQL,或者您可以从数据库扩展设置中启用扩展。(也可使用国内版supabase)
由于 PostGIS 的计算量可能很大,我们建议在专用的单独模式上启用它,例如名为gis!
CREATE SCHEMA IF NOT EXISTS "gis";
CREATE EXTENSION IF NOT EXISTS "postgis" WITH SCHEMA "gis";
将打开的地图数据导入到placesSupabase中的表中:
psql -h aws-0-us-west-1.pooler.supabase.com -p 5432 -d postgres -U postgres.project-ref < places.sql
您可以在Supabase 仪表板的数据库设置中找到凭据。
启用 RLS 并创建公共读取策略
我们希望地点数据可以公开获取,因此我们可以创建一个允许公开读取的行级安全策略。
在您的 Supabase 仪表板中,导航到SQL 编辑器并运行以下命令:
ALTER TABLE "public"."places" ENABLE ROW LEVEL SECURITY;CREATE POLICY "Enable read access for all users" ON "public"."places" FOR SELECT USING (true);
使用PostGIS生成矢量图块
为了在客户端请求时以编程方式生成矢量图块,我们需要创建一个 Postgres 函数,可以通过远程过程调用来调用它。在 SQL 编辑器中,运行:
CREATE OR REPLACE FUNCTION mvt(z integer, x integer, y integer)
RETURNS text
LANGUAGE plpgsql
AS $$
DECLAREmvt_output text;
BEGINWITH-- Define the bounds of the tile using the provided Z, X, Y coordinatesbounds AS (SELECT ST_TileEnvelope(z, x, y) AS geom),-- Transform the geometries from EPSG:4326 to EPSG:3857 and clip them to the tile boundsmvtgeom AS (SELECT-- include the name and id only at zoom 13 to make low-zoom tiles smallerCASEWHEN z > 13 THEN idELSE NULLEND AS id,CASEWHEN z > 13 THEN names::json->>'primary'ELSE NULLEND AS primary_name,categories::json->>'main' as main_category,ST_AsMVTGeom(ST_Transform(wkb_geometry, 3857), -- Transform the geometry to Web Mercatorbounds.geom,4096, -- The extent of the tile in pixels (commonly 256 or 4096)0, -- Buffer around the tile in pixelstrue -- Clip geometries to the tile extent) AS geomFROMplaces, boundsWHEREST_Intersects(ST_Transform(wkb_geometry, 3857), bounds.geom))-- Generate the MVT from the clipped geometriesSELECT INTO mvt_output encode(ST_AsMVT(mvtgeom, 'places', 4096, 'geom'),'base64')FROM mvtgeom;RETURN mvt_output;
END;
$$;
为了限制通过网络发送的数据量,我们限制了矢量图块中包含的元数据量。例如,我们为缩放级别添加了一个条件,并且只有当用户放大到 13 级以上时才返回地名。
使用 supabase-js 从 MapLibre GL 客户端获取矢量瓦片
index.html您可以在GitHub上找到完整的代码。在这里,我们将重点介绍如何向 MapLibreGL 添加新协议,以通过 supabase-js 获取 bas64 编码的二进制矢量瓦片数据,以便 MapLibre GL 可以在用户与地图交互时获取和呈现数据:
index.html
const client = supabase.createClient('your-supabase-api-url', 'your-supabase-anon-key')function base64ToArrayBuffer(base64) {var binaryString = atob(base64)var bytes = new Uint8Array(binaryString.length)for (var i = 0; i < binaryString.length; i++) {bytes[i] = binaryString.charCodeAt(i)}return bytes
}maplibregl.addProtocol('supabase', async (params, abortController) => {const re = new RegExp(/supabase:\/\/(.+)\/(\d+)\/(\d+)\/(\d+)/)const result = params.url.match(re)const { data, error } = await client.rpc('mvt', {z: result[2],x: result[3],y: result[4],})const encoded = base64ToArrayBuffer(data)if (!error) {return { data: encoded }} else {throw new Error(`Tile fetch error:`)}
})
注册 supabase 协议后,我们现在可以将其添加到 MapLibre GL 源中,并放置在底图(如Protomaps)之上,例如:
index.html
// ...
const map = new maplibregl.Map({hash: true,container: 'map',style: {version: 8,glyphs: 'https://cdn.protomaps.com/fonts/pbf/{fontstack}/{range}.pbf',sources: {supabase: {type: 'vector',tiles: ['supabase://boston/{z}/{x}/{y}'],attribution: '© <a href="https://overturemaps.org">Overture Maps Foundation</a>',},protomaps: {type: 'vector',url: 'https://api.protomaps.com/tiles/v3.json?key=your-protomaps-api-key',attribution: 'Basemap © <a href="https://openstreetmap.org">OpenStreetMap</a>',},},},
})
// ...
按需获取额外的 JSON 元数据
为了限制通过网络发送的数据量,我们不会对矢量图块本身中的所有元数据进行编码,而是设置一个 onclick 处理程序以在 MapLibre GL 弹出窗口中按需获取其他元数据:
index.html
// ..
const popup = new maplibregl.Popup({closeButton: true,closeOnClick: false,maxWidth: 'none',
})function loadDetails(element, id) {element.innerHTML = 'loading...'client.from('places').select(`websites,socials,phones,addresses,source: sources->0->dataset`).eq('id', id).single().then(({ data, error }) => {if (error) return console.error(error)element.parentElement.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`})
}map.on('click', 'overture-pois-text', async (e) => {if (e.features.length > 0) {const feature = e.features[0]console.log(feature)popup.setHTML(`<table style="font-size:12px"><tr><td>id:</td><td>${feature.properties.id}</td></tr><tr><td>name:</td><td>${feature.properties.primary_name}</td></tr><tr><td>main_category:</td><td>${feature.properties.main_category}</td></tr><tr><td>details:</td><td><span οnclick="loadDetails(this, '${feature.properties.id}')">load details</span></td></tr></table>`)popup.setLngLat(e.lngLat)popup.addTo(map)}
})
// ...
结论
PostGIS 功能强大,可让您以编程方式从存储在 Postgres 中的表行生成矢量图块。与 Supabase 自动生成的 REST API 和 supabase-js 客户端库配合使用,您可以轻松构建交互式地理空间应用程序!
更多 Supabase
- 观看视频指南
- 国内版suapbase
- 查找代码
- 使用 Protomaps 在 Supabase 存储上自托管地图
- PostGIS 入门
- PostGIS 文档指南
原文章:https://supabase.com/blog/postgis-generate-vector-tiles
相关文章:
使用 PostGIS 生成矢量图块
您喜欢视听学习吗?观看视频指南! 或者直接跳到代码 Overture Maps Foundation是由亚马逊、Meta、微软和 tomtom 发起的联合开发基金会项目,旨在创建可靠、易于使用、可互操作的开放地图数据。 Overture Maps 允许我们以GeoJSON格式下载开放…...
WebSocket 心跳机制如何实现
是一种简单并且有效的策略,用于维持长链接的活跃状态,防止因为网络空闲或者不稳定因素,导致链接意外中断。通过周期性的心跳消息,确保了链接的持久性和周期性,是维持实时通信服务稳定运行的关键组件。 1. 定时发送心跳…...
Docker 容器连接
Docker 容器连接 引言 在当今的软件开发和运维领域,Docker 已经成为了一个不可或缺的工具。它通过容器化的方式,为开发者提供了一种轻量级、可移植的计算环境。然而,要充分发挥 Docker 的潜力,我们需要掌握如何连接这些容器。本文将深入探讨 Docker 容器连接的概念、方法…...
【C语言】continue 关键字
当在C语言中使用continue关键字时,它用于控制循环语句的执行流程。与break不同,continue不会终止整个循环,而是终止当前迭代,并立即开始下一次迭代。这种行为使得可以在循环内部根据特定条件跳过某些代码块,从而控制程…...
Taro + vue3 中微信小程序中实现拉起支付
在前端开发中 H5 的拉起支付和微信小程序的拉起支付 是不太一样的 现在分享一下微信小程序中的拉起支付 逻辑都在后端 我是用的Taro 框架 其实就是一个Api Taro 文档...
003-GeoGebra如何无缝嵌入到PPT里
GeoGebra无缝嵌入到PPT里真是一个头疼的问题,已成功解决,这里记录一下,希望可以帮助到更多人。 注意,后续所有的文章说的PPT都是Offce Power Point, 不要拿着WPS的bug来问我哦,我已经戒WPS了(此处表示无奈&…...
AI:开发者的朋友还是对手?
AI是在帮助开发者还是取代他们? 在软件开发领域,生成式人工智能(AIGC)正在改变开发者的工作方式。无论是代码生成、错误检测还是自动化测试,AI工具正在成为开发者的得力助手。然而,这也引发了对开发者职业…...
如何在Android Studio中查看APP客户端日志
测试Android应用时,日志查看是一个至关重要的调试工具,它帮助测试人员快速定位问题。幸运的是,Android Studio为我们提供了一个强大的工具——Logcat,使得查看运行时日志变得直接且简单。本文将引导你如何在Android Studio中使用L…...
2024微信小程序期末大作业-点奶茶微信小程序(后端nodejs-server)(附下载链接)_微信小程序期末大作业百度网盘下载
菜单展示 购物车展示: 提交订单: 支付详情页展示: 订单查看: 查看历史消费: 部分代码展示: <!--pages/home/home.wxml--> <block wx:for"{{listData}}" wx:key"itemlist&qu…...
Qt:4.信号和槽
目录 1.信号源、信号和槽: 2.Qt类的继承关系: 3.自定义槽函数: 4.第一种信号和槽的连接的方法: 5.第二种信号和槽的连接的方法: 6.自定义信号: 7.发射信号: 8.信号和槽的传参:…...
Ubuntu20.04更新GLIBC到2.35版本
目录 1 背景2 增加源2.1 标准源2.2 镜像源 3 更新 1 背景 Ubuntu20.04默认GLIBC库版本是2.31.今天碰到一个软件需要2.35版本的GLIBC。 升级GLIBC库有两种方式: 下载高版本库源码,编译后替换系统中低版本库。由于GLIBC库是Linux系统中最基础库ÿ…...
Qt 实战(7)元对象系统 | 7.1、简介
文章目录 一、简介1、元对象系统的基本条件2、元对象系统的核心功能3、元对象系统的核心类4、总结 Qt的元对象系统(Meta-Object System)是Qt框架中一个极其重要的组成部分,它为Qt提供了信号与槽机制、实时类型信息(RTTI࿰…...
iOS 真机打包,证书报错No signing certificate “iOS Distribution” found
之前将APP从旧账号转移到了新账号,在新账号打包的时候遇到的证书问题。 因为新账号还没有导出“本地签名证书”,也还没有创建新的“发布证书”。当我创建好这两者之后,在xcode打包的时候就报错了。 报错信息: No signing certifi…...
2024年7月3日 (周三) 叶子游戏新闻
老板键工具来唤去: 它可以为常用程序自定义快捷键,实现一键唤起、一键隐藏的 Windows 工具,并且支持窗口动态绑定快捷键(无需设置自动实现)。 卸载工具 HiBitUninstaller: Windows上的软件卸载工具 《魅魔》新DLC《Elysian Fields…...
linux守护进程生命周期管理-supervisord
简介 supervisor是一个client/server系统,允许用户控制多个类unix系统的进程,摆脱rc.d脚本的不方便性.supervisor具有简单,集中化管理,搞笑,可扩展性,高兼容. 整套软件包含:supervisord(守护进程),supervisorctl(命令行工具),web server(一个web交互界面),XML-RPC 交互 安装 …...
rtpengine_mr12.0 基础建设容器运行
目录 Dockerfile rtpengine.conf 容器内编译安装 RTPEngine 正常提供功能 1. 启动RTPEngine服务 2. 删除 RTPEngine服务 3. 加载内核模块 检查所有进程是否正在运行 上传到仓库 博主wx:yuanlai45_csdn 博主qq:2777137742 后期会创建粉丝群&…...
逐步深入:掌握sklearn中的增量学习
🚀 逐步深入:掌握sklearn中的增量学习 在机器学习领域,增量学习(也称为在线学习)是一种重要的学习方式,它允许模型在新数据到来时进行更新,而不需要重新训练整个数据集。这对于处理大量数据或实…...
【机器学习】机器学习与图像识别的融合应用与性能优化新探索
文章目录 引言第一章:机器学习在图像识别中的应用1.1 数据预处理1.1.1 数据清洗1.1.2 数据归一化1.1.3 数据增强 1.2 模型选择1.2.1 卷积神经网络1.2.2 迁移学习1.2.3 混合模型 1.3 模型训练1.3.1 梯度下降1.3.2 随机梯度下降1.3.3 Adam优化器 1.4 模型评估与性能优…...
Unity射击游戏开发教程:(29)躲避敌人的子弹射击
在这篇文章中,我将介绍如何创建一个可以使玩家火力无效的敌人。创建的行为如下...... 当玩家向敌人开火时,敌人会向左或向右移动。向左或向右的移动是随机选择的,并在一段时间后停止敌人的移动。如果敌人移出屏幕,它就会绕到另一边。将一个精灵拖到画布上,将其缩小以匹配游…...
SpringCloud Gateway 网关获取或修改接口响应数据
文章目录 前言一、获取响应数据并打印 前言 我们的网关在之前只记录了接口请求日志,响应日志是没有做记录的,在后续别人对接我们开放平台时出现了一些问题没法确认当时的一个数值状态,照成了很多不必要的麻烦,后来决定在网关添加上…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
关于easyexcel动态下拉选问题处理
前些日子突然碰到一个问题,说是客户的导入文件模版想支持部分导入内容的下拉选,于是我就找了easyexcel官网寻找解决方案,并没有找到合适的方案,没办法只能自己动手并分享出来,针对Java生成Excel下拉菜单时因选项过多导…...
python打卡第47天
昨天代码中注意力热图的部分顺移至今天 知识点回顾: 热力图 作业:对比不同卷积层热图可视化的结果 def visualize_attention_map(model, test_loader, device, class_names, num_samples3):"""可视化模型的注意力热力图,展示模…...
C++11 constexpr和字面类型:从入门到精通
文章目录 引言一、constexpr的基本概念与使用1.1 constexpr的定义与作用1.2 constexpr变量1.3 constexpr函数1.4 constexpr在类构造函数中的应用1.5 constexpr的优势 二、字面类型的基本概念与使用2.1 字面类型的定义与作用2.2 字面类型的应用场景2.2.1 常量定义2.2.2 模板参数…...
