使用 Protomaps 和 Supabase 存储自托管地图

Protomaps是一个开源的世界地图,可作为单个静态文件部署在Supabase Storage上。

在本教程中,您将学习

  • 使用 Protomaps 将某个区域提取到静态 PMTiles 文件中。
  • 将 PMTiles 文件上传到 Supabase 存储。
  • 使用 MapLibre 将地图呈现到网页上。
  • 使用 Supabase Edge Functions 来限制文件访问。

将某个区域提取到静态 PMTiles 文件中#

Protomaps 提供了一个pmtilesCLI,可用于从世界地图中剪切出某些区域并将其压缩为单个静态文件。

例如,我们可以像这样提取荷兰乌得勒支周围的一小块区域:

pmtiles extract https://build.protomaps.com/20240618.pmtiles my_area.pmtiles --bbox=5.068050,52.112086,5.158424,52.064140

注意:请确保将日期更新为最新的每日构建!

这将创建一个my_area.pmtiles您可以上传到 Supabase Storage 的文件。

将 PMTiles 文件上传到 Supabase 存储#

在您的Supabase 仪表板中导航到Storage并单击“新存储桶”并创建一个名为 的新公共存储桶public-maps

将之前创建的文件上传my_area.pmtiles到您的公共存储桶。上传后,单击文件并点击“获取 URL”。

Supabase Storage 开箱即用地支持所需的HTTP 范围请求,允许您直接从地图客户端使用公共存储 URL。

使用 MapLibre 渲染地图

PMTiles 可轻松与MapLibre GLLeaflet配合使用。在我们的示例中,我们将使用MapLibre GL,这是一个 TypeScript 库,它使用 WebGL 在浏览器中从矢量图块渲染交互式地图。

这是一个使用库的 CDN 版本的原生 JS 示例。您可以非常轻松地将其调整为与 React 配合使用,例如使用react-map-gl库。

index.html

<html>
  <head>
    <title>Overture Places</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      rel="stylesheet"
      href="https://unpkg.com/maplibre-gl@4.1.2/dist/maplibre-gl.css"
      crossorigin="anonymous"
    />
    <script
      src="https://unpkg.com/maplibre-gl@4.1.2/dist/maplibre-gl.js"
      crossorigin="anonymous"
    ></script>
    <script src="https://unpkg.com/protomaps-themes-base@2.0.0-alpha.5/dist/index.js"></script>
    <script src="https://unpkg.com/pmtiles@3.0.6/dist/pmtiles.js"></script>
    <style>
      body {
        margin: 0;
      }
      #map {
        height: 100%;
        width: 100%;
      }
    </style>
  </head>
  <body>
    <div id="map"></div>
    <script type="text/javascript">
      // Add the PMTiles Protocol:
      let protocol = new pmtiles.Protocol()
      maplibregl.addProtocol('pmtiles', protocol.tile)

      // Load the Map tiles directly from Supabase Storage:
      const map = new maplibregl.Map({
        hash: true,
        container: 'map',
        style: {
          version: 8,
          glyphs: 'https://cdn.protomaps.com/fonts/pbf/{fontstack}/{range}.pbf',
          sources: {
            protomaps: {
              attribution:
                '<a href="https://github.com/protomaps/basemaps">Protomaps</a> © <a href="https://openstreetmap.org">OpenStreetMap</a>',
              type: 'vector',
              url: 'pmtiles://https://<your-project-ref>.supabase.co/storage/v1/object/public/public-maps/my_area.pmtiles',
            },
          },
          layers: protomaps_themes_base.default('protomaps', 'dark'),
        },
      })
    </script>
  </body>
</html>

使用 Supabase Edge Functions 限制访问#

公共 Supabase Storage 存储桶允许从任何来源进行访问,这可能并不适合您的使用情况。在撰写本文时,您无法修改 Supabase Storage 存储桶的 CORS 设置,但是您可以使用Supabase Edge Functions来限制对 PMTiles 文件的访问,例如,您甚至可以将其与Supabase Auth配对以限制对某些用户的访问。

在 Supabase 仪表板中,创建一个名为 的新私有存储桶,maps-private并将my_area.pmtiles文件上传到其中。私有存储桶中的文件只能通过短期签名 URL 或通过将秘密服务角色密钥作为授权标头传递来访问。由于我们的 Edge Function 是一个安全的服务器端环境,因此我们可以在此处使用后一种方法。

使用Supabase CLI,通过运行创建一个新的 Edge Function supabase functions new maps-private,然后将以下代码添加到新创建的函数中:

supabase/functions/maps-private/ index.ts

const ALLOWED_ORIGINS = ['http://localhost:8000']
const corsHeaders = {
  'Access-Control-Allow-Origin': ALLOWED_ORIGINS.join(','),
  'Access-Control-Allow-Headers':
    'authorization, x-client-info, apikey, content-type, range, if-match',
  'Access-Control-Expose-Headers': 'range, accept-ranges, etag',
  'Access-Control-Max-Age': '300',
}

Deno.serve((req) => {
  // This is needed if you're planning to invoke your function from a browser.
  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders })
  }

  // Check origin
  const origin = req.headers.get('Origin')

  if (!origin || !ALLOWED_ORIGINS.includes(origin)) {
    return new Response('Not Allowed', { status: 405 })
  }

  const reqUrl = new URL(req.url)
  const url = `${Deno.env.get('SUPABASE_URL')}/storage/v1/object/authenticated${reqUrl.pathname}`

  const { method, headers } = req
  // Add Auth header
  const modHeaders = new Headers(headers)
  modHeaders.append('authorization', `Bearer ${Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!}`)
  return fetch(url, { method, headers: modHeaders })
})

如果您想根据经过身份验证的用户进一步限制访问,您可以将 Edge Function 与 Supabase Auth 配对,如本例所示。

最后,我们需要通过运行将我们的 Edge Function 部署到 Supabase 。请注意,如果您想允许从您的网站进行公共访问而无需任何 Supabase Auth 用户,则需要supabase functions deploy maps-private --no-verify-jwt--no-verify-jwt标志。

现在,我们可以简单地用我们的 Edge Functions URL 替换公共存储 URL,以将范围请求代理到我们的私有存储桶:

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: {
      protomaps: {
        attribution:
          '<a href="https://github.com/protomaps/basemaps">Protomaps</a> © <a href="https://openstreetmap.org">OpenStreetMap</a>',
        type: 'vector',
        url: 'pmtiles://https://<project_ref>.supabase.co/functions/v1/maps-private/my_area.pmtiles',
      },
    },
    layers: protomaps_themes_base.default('protomaps', 'dark'),
  },
})
// ...

现在继续提供你的index.html文件,例如通过 Python SimpleHTTPServer:并在localhost:8000python3 -m http.server上欣赏你美丽的地图!

结论

Protomaps 是一个很棒的开源项目,它允许您在 Supabase Storage 上托管您自己的 Google Maps 替代方案。您可以使用强大的 PostGIS 功能进一步扩展它,以编程方式生成矢量图块,我们将在本系列的下一篇文章中探讨这一点。所以请务必订阅我们的TwitterYouTube频道,不要错过!到时候见!

更多 Supabase #

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值