Postgres 与 MapLibre 实时位置共享

本教程以之前对 Postgis 和 Supabase 的学习为基础,并在此基础上添加了 Supabase Realtime。如果您是此主题的新手,我们建议您先查看以下内容:

可以使用国内版supabase,作为supabase替代。

在本教程中,您将学习

  • 使用 Supabase Edge Function 构建一个可捕获实时位置数据的 Telegram Bot。
  • 使用 RPC(远程过程调用)将位置数据从边缘函数插入 Postgres。
  • 使用 Supabase Realtime 监听数据库的变化。
  • 在 React 中使用 MapLibre GL JS 将实时位置数据绘制到地图上。

使用 Edge Functions 将位置数据写入 Supabase

在本部分中,您将创建一个 Edge Function,用于从 Telegram Bot 捕获实时位置数据。Telegram Bot 将位置数据发送到 Edge Function,然后 Edge Function 将数据插入 Supabase。

有关如何创建 Telegram Bot 的详细指南,请参阅此处的文档。

您可以在GitHub上找到可用于生产的 Telegram Bot Supabase Edge Function 代码。这是监听实时位置更新并将其写入数据库的相关代码:

/supabase/functions/telegram-bot/index.ts​

import { Bot, webhookCallback } from 'https://deno.land/x/grammy@v1.20.3/mod.ts'
import { createClient } from 'jsr:@supabase/supabase-js@2.39.7'
import { Database } from '../_shared/database.types.ts'

const token = Deno.env.get('BOT_TOKEN')
if (!token) throw new Error('BOT_TOKEN is unset')

const supabase = createClient<Database>(
  Deno.env.get('SUPABASE_URL')!,
  Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)

const bot = new Bot(token)
// ...

bot.on('edit:location', async (ctx) => {
  const {
    location,
    from: { id: user_id },
    edit_date,
  } = ctx.update.edited_message!
  if (location) {
    // Insert into db
    const { error } = await supabase.rpc('location_insert', {
      _user_id: user_id,
      _lat: location.latitude,
      _long: location.longitude,
      _timestamp: edit_date,
    })
    if (
      error &&
      error.message !==
        'null value in column "event_id" of relation "locations" violates not-null constraint' &&
      error.message !== 'duplicate key value violates unique constraint "locations_pkey"'
    ) {
      return console.log(`edit:location:insert:error:user:${user_id}: ${error.message}`)
    }
  }
  return
})

const handleUpdate = webhookCallback(bot, 'std/http')

Deno.serve(async (req) => {
  const headers = req.headers
  try {
    const url = new URL(req.url)
    if (url.searchParams.get('secret') !== Deno.env.get('FUNCTION_SECRET')) {
      return new Response('not allowed', { status: 405 })
    }

    return await handleUpdate(req)
  } catch (err) {
    console.log(headers)
    console.error(err)
  }
  return new Response()
})

使用 RPC 将位置数据插入Postgres

上面的边缘函数使用 RPC(远程过程调用)将位置数据插入数据库。RPC 在我们的Supabase Migrations中定义。RPC 首先验证用户是否有活动会话,然后将位置数据插入表中locations

CREATE OR REPLACE FUNCTION public.location_insert(_timestamp bigint, _lat double precision, _long double precision, _user_id bigint)
RETURNS void AS $$
declare active_event_id uuid;
begin
  select event_id into active_event_id from public.sessions where user_id = _user_id and status = 'ACTIVE'::session_status;

  INSERT INTO public.locations(event_id, user_id, created_at, lat, long, location)
  VALUES (active_event_id, _user_id, to_timestamp(_timestamp), _lat, _long, st_point(_long, _lat));
end;
$$ LANGUAGE plpgsql VOLATILE;

使用 Supabase Realtime 监听数据库的变化

在本节中,您将使用 Supabase Realtime 监听数据库中的更改。Realtime API 是一个功能强大的工具,可让您将数据库中的更改广播到多个客户端。

用于监听实时变化并在地图上绘制标记的完整客户端代码可在GitHub上找到。

我们将把它分为几个步骤:

由于我们在 React 中工作,我们将在钩子中设置实时订阅useEffect。如果您使用的是 Next.js,则必须标记这一点,use client因为我们需要客户端 JavaScript 来实现这一点:

/app/app/realtimemap/%5Bevent%5D/page.tsx

// ...
export default function Page({ params }: { params: { event: string } }) {
  const supabase = createClient<Database>()
  const [locations, setLocations] = useState<{
    [key: string]: Tables<'locations'>
  } | null>(null)
  const locationsRef = useRef<{
    [key: string]: Tables<'locations'>
  } | null>()
  locationsRef.current = locations

  useEffect(() => {
    // Listen to realtime updates
    const subs = supabase
      .channel('schema-db-changes')
      .on(
        'postgres_changes',
        {
          event: 'INSERT', // Listen only to INSERTs
          schema: 'public',
          table: 'locations',
          filter: `event_id=eq.${params.event}`,
        },
        (payload) => {
          const loc = payload.new as Tables<'locations'>
          const updated = {
            ...locationsRef.current,
            [loc.user_id.toString()]: loc,
          }

          setLocations(updated)
        }
      )
      .subscribe()
    console.log('Subscribed')

    return () => {
      subs.unsubscribe()
    }
  }, [])
// ...

在 React 中使用 MapLibre GL JS 将实时位置数据绘制到地图上

locations上面的实时订阅监听器会在对象插入到表中时使用新的位置数据更新对象的状态。现在我们可以react-map-gl轻松地将位置标记绘制到地图上:

/app/app/realtimemap/%5Bevent%5D/page.tsx

// ...
<Map
  className="map"
  cooperativeGestures={true}
  initialViewState={{
    longitude: 103.852713,
    latitude: 1.285727,
    zoom: 13,
  }}
  mapStyle={{
    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',
      },
    },
    transition: {
      duration: 0,
    },
    // @ts-ignore
    layers: layers('protomaps', 'light'),
  }}
  // @ts-ignore
  mapLib={maplibregl}
>
  {Object.entries(locations).map(([key, value]) => (
    <Marker key={key} longitude={value.long} latitude={value.lat} color="red" />
  ))}
</Map>

请注意,我们在此使用托管在 Supabase Storage 上的Protomaps作为基础地图。要了解此内容,请阅读我们之前的教程

就是这样,使用 Supabase 向您的应用程序添加实时位置数据就是这么简单!我们迫不及待地想看看您将构建什么!

结论

Supabase Realtime 非常适合向多个客户端广播位置数据。结合 PostGIS 的强大功能和更广泛的 Postgres 扩展生态系统,它是满足您所有地理空间需求的强大解决方案!

想要了解有关地图和 PostGIS 的更多信息?请务必关注我们的TwitterYouTube频道,以免错过!到时候见!

更多 Supabase #

相关推荐

  1. Postgres MapLibre 实时位置共享

    2024-07-10 19:58:04       18 阅读
  2. 实用SQL语句(postgres)

    2024-07-10 19:58:04       53 阅读
  3. postgres

    2024-07-10 19:58:04       54 阅读
  4. NFS文件共享存储详解实战

    2024-07-10 19:58:04       52 阅读
  5. PostGISPOSTGIS实现聚类统计提取外轮廓

    2024-07-10 19:58:04       63 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-10 19:58:04       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-10 19:58:04       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-10 19:58:04       58 阅读
  4. Python语言-面向对象

    2024-07-10 19:58:04       69 阅读

热门阅读

  1. NestJs实现各种请求与参数解析

    2024-07-10 19:58:04       26 阅读
  2. AHK的对象和类学习心得

    2024-07-10 19:58:04       19 阅读
  3. Spring中常见知识点及使用

    2024-07-10 19:58:04       27 阅读
  4. Uniapp的简要开发流程指南

    2024-07-10 19:58:04       23 阅读
  5. LeetCode //C - 204. Count Primes

    2024-07-10 19:58:04       21 阅读
  6. 【debug】keras使用基础问题

    2024-07-10 19:58:04       18 阅读
  7. Qt 绘图详解

    2024-07-10 19:58:04       23 阅读
  8. MySQL篇七:复合查询

    2024-07-10 19:58:04       26 阅读
  9. [GDOUCTF 2023]Tea writeup

    2024-07-10 19:58:04       27 阅读
  10. 软件开发C#(Sharp)总结(续)

    2024-07-10 19:58:04       17 阅读