Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(hackathon-3000): use protomap tiles from s3 #18247

Merged
merged 15 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .env.example

This file was deleted.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion frontend/src/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 8 additions & 17 deletions frontend/src/lib/components/Map/Map.stories.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof Map> = {
title: 'Components/Map',
component: Map,
tags: ['autodocs'],
}
type Story = StoryObj<typeof Map>

const coordinates: [number, number] = [0.119167, 52.205276]

export const Unavailable: Story = {}

export const Basic: Story = {
render: (args) => (
<MapComponent
mapLibreStyleUrl="" // TODO: set this value for the publish storybook and visual regression tests
{...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<typeof Map>

export const Basic: Story = {}

export default meta
49 changes: 37 additions & 12 deletions frontend/src/lib/components/Map/Map.tsx
Original file line number Diff line number Diff line change
@@ -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. */
Expand All @@ -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<MapProps, 'mapLibreStyleUrl'>): JSX.Element {
if (!window.JS_MAPLIBRE_STYLE_URL) {
export function Map({ className, ...rest }: MapProps): JSX.Element {
const { isCloudOrDev } = useValues(preflightLogic)

if (!isCloudOrDev) {
return (
<div className={`w-full h-full flex flex-col items-center justify-center text-muted p-3 ${className}`}>
<h1>Map unavailable</h1>
<p>
The <code>MAPLIBRE_STYLE_URL</code> setting is not defined. Please configure this setting with a
valid MapLibre Style URL to display maps.
</p>
<p>The map is currently only available in cloud deployments.</p>
</div>
)
}

return <MapComponent mapLibreStyleUrl={window.JS_MAPLIBRE_STYLE_URL} className={className} {...rest} />
return <MapComponent className={className} {...rest} />
}

export function MapComponent({ center, markers, className, mapLibreStyleUrl }: MapProps): JSX.Element {
export function MapComponent({ center, markers, className }: MapProps): JSX.Element {
const mapContainer = useRef<HTMLDivElement>(null)
const map = useRef<RawMap | null>(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:
'<a href="https://protomaps.com">Protomaps</a> © <a href="https://openstreetmap.org">OpenStreetMap</a>',
},
},
layers: layers('protomaps', isDarkModeOn ? 'dark' : 'light'),
},
center,
zoom: 4,
maxZoom: 10,
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/scenes/PreflightCheck/preflightLogic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ export const preflightLogic = kea<preflightLogicType>([
}))
},
],
isCloudOrDev: [
(s) => [s.preflight],
(preflight): boolean | undefined => {
return preflight?.cloud || preflight?.is_debug
},
],
}),
listeners(({ values, actions }) => ({
handlePreflightFinished: () => {
Expand Down
17 changes: 13 additions & 4 deletions frontend/src/scenes/persons/PersonFeedCanvas.tsx
Original file line number Diff line number Diff line change
@@ -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]
Expand All @@ -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() },
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
20 changes: 20 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions posthog/settings/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
5 changes: 0 additions & 5 deletions posthog/templates/head.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@
window.SENTRY_ENVIRONMENT = '{{ sentry_environment | escapejs }}';
</script>
{% endif %}
{% if js_maplibre_style_url %}
<script>
window.JS_MAPLIBRE_STYLE_URL = '{{ js_maplibre_style_url | escapejs }}'
</script>
{% endif %}
<script id='posthog-app-user-preload'>
window.POSTHOG_APP_CONTEXT = JSON.parse("{{ posthog_app_context | escapejs }}");
// Inject the expected location of JS bundle, use to allow the location of
Expand Down
3 changes: 0 additions & 3 deletions posthog/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,9 +344,6 @@ def render_template(
context["js_kea_verbose_logging"] = settings.KEA_VERBOSE_LOGGING
context["js_url"] = get_js_url(request)

if settings.MAPLIBRE_STYLE_URL:
context["js_maplibre_style_url"] = settings.MAPLIBRE_STYLE_URL

posthog_app_context: Dict[str, Any] = {
"persisted_feature_flags": settings.PERSISTED_FEATURE_FLAGS,
"anonymous": not request.user or not request.user.is_authenticated,
Expand Down
Loading