diff --git a/.env.example b/.env.example deleted file mode 100644 index 65971db798cd3..0000000000000 --- a/.env.example +++ /dev/null @@ -1 +0,0 @@ -MAPLIBRE_STYLE_URL=https://api.example.com/style.json?key=mykey \ No newline at end of file diff --git a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist.png b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist.png index 7cc0f8cd4b420..44e5a61e789a2 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist.png and b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist.png differ diff --git a/frontend/src/globals.d.ts b/frontend/src/globals.d.ts index df0e0e81664ea..17bc65680725a 100644 --- a/frontend/src/globals.d.ts +++ b/frontend/src/globals.d.ts @@ -6,7 +6,6 @@ declare global { JS_POSTHOG_API_KEY?: string JS_POSTHOG_HOST?: string JS_POSTHOG_SELF_CAPTURE?: boolean - JS_MAPLIBRE_STYLE_URL?: string JS_CAPTURE_TIME_TO_SEE_DATA?: boolean JS_KEA_VERBOSE_LOGGING?: boolean posthog?: posthog diff --git a/frontend/src/lib/components/Map/Map.stories.tsx b/frontend/src/lib/components/Map/Map.stories.tsx index 09576ec0ed50c..41807351e6367 100644 --- a/frontend/src/lib/components/Map/Map.stories.tsx +++ b/frontend/src/lib/components/Map/Map.stories.tsx @@ -1,31 +1,22 @@ import type { Meta, StoryObj } from '@storybook/react' -import { Marker } from 'maplibre-gl' -import { Map, MapComponent } from './Map' +import { Map } from './Map' + +const coordinates: [number, number] = [0.119167, 52.205276] const meta: Meta = { title: 'Components/Map', component: Map, tags: ['autodocs'], -} -type Story = StoryObj - -const coordinates: [number, number] = [0.119167, 52.205276] - -export const Unavailable: Story = {} - -export const Basic: Story = { - render: (args) => ( - - ), + // :TRICKY: We can't use markers in Storybook stories, as the Marker class is + // not JSON-serializable (circular structure). args: { center: coordinates, - markers: [new Marker({ color: 'var(--primary)' }).setLngLat(coordinates)], className: 'h-60', }, } +type Story = StoryObj + +export const Basic: Story = {} export default meta diff --git a/frontend/src/lib/components/Map/Map.tsx b/frontend/src/lib/components/Map/Map.tsx index 0b72d136d04d4..0ef4efb2d7ecc 100644 --- a/frontend/src/lib/components/Map/Map.tsx +++ b/frontend/src/lib/components/Map/Map.tsx @@ -1,9 +1,23 @@ import { useEffect, useRef } from 'react' -import { Map as RawMap, Marker } from 'maplibre-gl' +import { useValues } from 'kea' +import maplibregl, { Map as RawMap, Marker } from 'maplibre-gl' +import { Protocol } from 'pmtiles' +import layers from 'protomaps-themes-base' import useResizeObserver from 'use-resize-observer' import 'maplibre-gl/dist/maplibre-gl.css' +import { themeLogic } from '~/layout/navigation-3000/themeLogic' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + +const protocol = new Protocol() +maplibregl.addProtocol('pmtiles', protocol.tile) + +const BASE_URL = 'https://posthog-prod-maps.s3.us-east-1.amazonaws.com' +// :TRICKY: The URL absolutely needs to be prefixed with `pmtiles://` to work! +const PMTILES_URL = `pmtiles://${BASE_URL}/20230913.pmtiles` +const GLYPH_URL = `${BASE_URL}/fonts/pbf/{fontstack}/{range}.pbf` + /** Latitude and longtitude in degrees (+lat is east, -lat is west, +lon is south, -lon is north). */ export interface MapProps { /** Coordinates to center the map on by default. */ @@ -12,34 +26,45 @@ export interface MapProps { markers?: Marker[] /** Map container class names. */ className?: string - /** The map's MapLibre style. This must be a JSON object conforming to the schema described in the MapLibre Style Specification, or a URL to such JSON. */ - mapLibreStyleUrl: string } -export function Map({ className, ...rest }: Omit): JSX.Element { - if (!window.JS_MAPLIBRE_STYLE_URL) { +export function Map({ className, ...rest }: MapProps): JSX.Element { + const { isCloudOrDev } = useValues(preflightLogic) + + if (!isCloudOrDev) { return (

Map unavailable

-

- The MAPLIBRE_STYLE_URL setting is not defined. Please configure this setting with a - valid MapLibre Style URL to display maps. -

+

The map is currently only available in cloud deployments.

) } - return + return } -export function MapComponent({ center, markers, className, mapLibreStyleUrl }: MapProps): JSX.Element { +export function MapComponent({ center, markers, className }: MapProps): JSX.Element { const mapContainer = useRef(null) const map = useRef(null) + const { isDarkModeOn } = useValues(themeLogic) + useEffect(() => { map.current = new RawMap({ container: mapContainer.current as HTMLElement, - style: mapLibreStyleUrl, + style: { + version: 8, + glyphs: GLYPH_URL, + sources: { + protomaps: { + type: 'vector', + url: PMTILES_URL, + attribution: + 'Protomaps © OpenStreetMap', + }, + }, + layers: layers('protomaps', isDarkModeOn ? 'dark' : 'light'), + }, center, zoom: 4, maxZoom: 10, diff --git a/frontend/src/scenes/PreflightCheck/preflightLogic.tsx b/frontend/src/scenes/PreflightCheck/preflightLogic.tsx index 9feb724ee05c3..83dfe166591f2 100644 --- a/frontend/src/scenes/PreflightCheck/preflightLogic.tsx +++ b/frontend/src/scenes/PreflightCheck/preflightLogic.tsx @@ -253,6 +253,12 @@ export const preflightLogic = kea([ })) }, ], + isCloudOrDev: [ + (s) => [s.preflight], + (preflight): boolean | undefined => { + return preflight?.cloud || preflight?.is_debug + }, + ], }), listeners(({ values, actions }) => ({ handlePreflightFinished: () => { diff --git a/frontend/src/scenes/persons/PersonFeedCanvas.tsx b/frontend/src/scenes/persons/PersonFeedCanvas.tsx index f8fef880bca53..fd3ebd2448934 100644 --- a/frontend/src/scenes/persons/PersonFeedCanvas.tsx +++ b/frontend/src/scenes/persons/PersonFeedCanvas.tsx @@ -1,12 +1,17 @@ +import { useValues } from 'kea' + import { PersonType } from '~/types' import { Notebook } from 'scenes/notebooks/Notebook/Notebook' import { uuid } from 'lib/utils' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' type PersonFeedCanvasProps = { person: PersonType } const PersonFeedCanvas = ({ person }: PersonFeedCanvasProps): JSX.Element => { + const { isCloudOrDev } = useValues(preflightLogic) + const id = person.id const personId = person.distinct_ids[0] @@ -32,10 +37,14 @@ const PersonFeedCanvas = ({ person }: PersonFeedCanvasProps): JSX.Element => { type: 'ph-person', attrs: { id: personId, nodeId: uuid(), title: 'Info' }, }, - { - type: 'ph-map', - attrs: { id: personId, nodeId: uuid() }, - }, + ...(isCloudOrDev + ? [ + { + type: 'ph-map', + attrs: { id: personId, nodeId: uuid() }, + }, + ] + : []), { type: 'ph-properties', attrs: { id: personId, nodeId: uuid() }, diff --git a/package.json b/package.json index f69a94361bc31..8f6d3566f8c1b 100644 --- a/package.json +++ b/package.json @@ -134,10 +134,12 @@ "md5": "^2.3.0", "monaco-editor": "^0.39.0", "papaparse": "^5.4.1", + "pmtiles": "^2.11.0", "posthog-js": "1.87.4", "posthog-js-lite": "2.0.0-alpha5", "prettier": "^2.8.8", "prop-types": "^15.7.2", + "protomaps-themes-base": "2.0.0-alpha.1", "query-selector-shadow-dom": "^1.0.0", "rc-field-form": "~1.21.0", "rc-picker": "~2.5.17", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ee42f08b7ff5..35689ba3d1828 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -215,6 +215,9 @@ dependencies: papaparse: specifier: ^5.4.1 version: 5.4.1 + pmtiles: + specifier: ^2.11.0 + version: 2.11.0 posthog-js: specifier: 1.87.4 version: 1.87.4 @@ -227,6 +230,9 @@ dependencies: prop-types: specifier: ^15.7.2 version: 15.8.1 + protomaps-themes-base: + specifier: 2.0.0-alpha.1 + version: 2.0.0-alpha.1 query-selector-shadow-dom: specifier: ^1.0.0 version: 1.0.0 @@ -10671,6 +10677,10 @@ packages: resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} dev: false + /fflate@0.8.1: + resolution: {integrity: sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ==} + dev: false + /figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} @@ -15082,6 +15092,12 @@ packages: semver-compare: 1.0.0 dev: true + /pmtiles@2.11.0: + resolution: {integrity: sha512-dU9SzzaqmCGpdEuTnIba6bDHT6j09ZJFIXxwGpvkiEnce3ZnBB1VKt6+EOmJGueriweaZLAMTUmKVElU2CBe0g==} + dependencies: + fflate: 0.8.1 + dev: false + /pngjs@3.4.0: resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==} engines: {node: '>=4.0.0'} @@ -15751,6 +15767,10 @@ packages: resolution: {integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==} dev: false + /protomaps-themes-base@2.0.0-alpha.1: + resolution: {integrity: sha512-eGAiUpBPAohnMvEHoF7NRWp7YuTNk/JsAVJ4733jqNw+/EF6Q5TMjqCOZScG3YSri5NStJg+9Upb95M2AQ3pjw==} + dev: false + /proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} diff --git a/posthog/settings/__init__.py b/posthog/settings/__init__.py index 099e1812e5311..5f915f3c1d6f5 100644 --- a/posthog/settings/__init__.py +++ b/posthog/settings/__init__.py @@ -85,9 +85,6 @@ # Whether kea should be act in verbose mode KEA_VERBOSE_LOGGING = get_from_env("KEA_VERBOSE_LOGGING", False, type_cast=str_to_bool) -# MapLibre Style URL to configure map tile source -MAPLIBRE_STYLE_URL = get_from_env("MAPLIBRE_STYLE_URL", optional=True) - # Only written in specific scripts - do not use outside of them. PERSON_ON_EVENTS_OVERRIDE = get_from_env("PERSON_ON_EVENTS_OVERRIDE", optional=True, type_cast=str_to_bool) diff --git a/posthog/templates/head.html b/posthog/templates/head.html index 7ca827ae15914..ed0d359faa014 100644 --- a/posthog/templates/head.html +++ b/posthog/templates/head.html @@ -36,11 +36,6 @@ window.SENTRY_ENVIRONMENT = '{{ sentry_environment | escapejs }}'; {% endif %} -{% if js_maplibre_style_url %} - -{% endif %}