Skip to content

Commit 21e5eb8

Browse files
committed
Use new tileserver
1 parent 83c03d8 commit 21e5eb8

File tree

1 file changed

+99
-236
lines changed

1 file changed

+99
-236
lines changed

pages/heatmap/+Page.client.ts

Lines changed: 99 additions & 236 deletions
Original file line numberDiff line numberDiff line change
@@ -3,276 +3,139 @@ import { useAPIResult } from "@macrostrat/ui-components";
33
import {
44
MapAreaContainer,
55
MapView,
6+
buildInspectorStyle
67
} from "@macrostrat/map-interface";
78
import { mapboxAccessToken, matomoToken, matomoApiURL } from "@macrostrat-web/settings";
89
import { Footer } from "~/components/general";
910
import { Divider, Spinner, Tabs, Tab } from "@blueprintjs/core";
1011
import { useEffect, useState } from "react";
12+
import { mergeStyles } from "@macrostrat/mapbox-utils";
13+
import { useDarkMode } from "@macrostrat/ui-components";
1114

1215

1316
export function Page() {
14-
const [data, setData] = useState<Array<{ lat: number; lng: number }> | null>(null);
15-
16-
useEffect(() => {
17-
const fetchData = async () => {
18-
const result = await fetchAllUsageStats();
19-
setData(result);
20-
};
21-
22-
fetchData();
23-
}, []);
24-
2517
return h('div.main', [
2618
h('div.heatmap-page', [
27-
h(PageHeader, { coords: data }),
28-
h(
29-
Tabs,
30-
{
31-
id: 'heatmap-tabs',
32-
},
33-
[
34-
h(Tab, { id: 'today', title: 'Today', panelClassName: 'today-tab-panel', panel: h(TodayMap, { data }) }),
35-
]
36-
)
19+
h(PageHeader),
20+
h(HeatMap)
3721
]),
3822
h(Footer)
3923
]);
4024
}
4125

42-
function PageHeader({ coords }) {
26+
function PageHeader() {
4327
return h('div.page-header', [
4428
h('h1', 'Heatmap'),
45-
h.if(coords?.length)('h3', `${coords?.length?.toLocaleString()} visits this year`),
4629
h(Divider),
4730
h('p', 'This is a heatmap of all the locations where Macrostrat has been accessed.'),
4831
h('p', 'The blue markers indicate today\'s accesses, while the grey markers indicate accesses from other days.'),
4932
]);
5033
}
5134

52-
function AllMap({coords, today}) {
53-
if (!coords || !today) {
54-
return h("div.map-area-container.loading", [
55-
h(Spinner, { size: 50 }),
56-
]);
57-
}
58-
59-
const handleMapLoaded = (map) => {
60-
map.on('load', () => {
61-
// Combine coords and today coords, marking today's points
62-
const allFeatures = coords.map((coord) => ({
63-
type: 'Feature',
64-
geometry: {
65-
type: 'Point',
66-
coordinates: [coord.longitude, coord.latitude],
67-
},
68-
properties: {
69-
isToday: false,
70-
},
71-
})).concat(
72-
today.map((coord) => ({
73-
type: 'Feature',
74-
geometry: {
75-
type: 'Point',
76-
coordinates: [coord.longitude, coord.latitude],
77-
},
78-
properties: {
79-
isToday: true,
80-
},
81-
}))
82-
);
83-
84-
map.addSource('markers', {
85-
type: 'geojson',
86-
data: {
87-
type: 'FeatureCollection',
88-
features: allFeatures,
89-
},
90-
});
91-
92-
// Individual points - others (grey)
93-
map.addLayer({
94-
id: 'markers-other',
95-
type: 'circle',
96-
source: 'markers',
97-
filter: ['all', ['!', ['has', 'point_count']], ['==', ['get', 'isToday'], false]],
98-
paint: {
99-
'circle-radius': 2,
100-
'circle-color': '#888',
101-
},
102-
});
103-
104-
// Individual points - today (blue)
105-
map.addLayer({
106-
id: 'markers-today',
107-
type: 'circle',
108-
source: 'markers',
109-
filter: ['all', ['!', ['has', 'point_count']], ['==', ['get', 'isToday'], true]],
110-
paint: {
111-
'circle-radius': 3,
112-
'circle-color': '#007cbf',
113-
},
114-
});
115-
});
116-
};
117-
118-
return h(MapInner, { handleMapLoaded });
119-
}
120-
121-
function MapInner({handleMapLoaded}) {
122-
const style = 'mapbox://styles/mapbox/dark-v10';
123-
124-
return h(
125-
MapAreaContainer,
126-
{
127-
className: "map-area-container",
128-
},
129-
[
130-
h(MapView, {
131-
style,
132-
mapboxToken: mapboxAccessToken,
133-
onMapLoaded: handleMapLoaded,
134-
mapPosition: {
135-
camera: {
136-
lat: 39,
137-
lng: -98,
138-
altitude: 6000000,
139-
},
140-
},
141-
}),
142-
]
143-
);
144-
}
145-
146-
function TodayMap({data}) {
147-
if (!data) {
148-
return h("div.map-area-container.loading", [
149-
h(Spinner, { size: 50 }),
150-
]);
151-
}
152-
153-
const handleMapLoaded = (map) => {
154-
map.on('load', () => {
155-
// Combine coords and today coords, marking today's points
156-
const allFeatures = data.map((coord) => ({
157-
type: 'Feature',
158-
geometry: {
159-
type: 'Point',
160-
coordinates: [coord.lng, coord.lat],
161-
},
162-
}))
163-
164-
map.addSource('markers', {
165-
type: 'geojson',
166-
data: {
167-
type: 'FeatureCollection',
168-
features: allFeatures,
169-
},
170-
});
171-
172-
map.addLayer({
173-
id: 'markers-today',
174-
type: 'circle',
175-
source: 'markers',
176-
paint: {
177-
'circle-radius': 3,
178-
'circle-color': '#007cbf',
179-
},
180-
});
181-
});
182-
};
183-
184-
return h(MapInner, { handleMapLoaded });
35+
function todayStyle() {
36+
return {
37+
sources: {
38+
today: {
39+
type: "vector",
40+
tiles: ["http://localhost:8000/usage-stats/rockd/{z}/{x}/{y}?today=true"],
41+
}
42+
},
43+
layers: [
44+
{
45+
id: 'today-points',
46+
type: 'circle',
47+
source: 'today',
48+
"source-layer": "default",
49+
paint: {
50+
'circle-color': "#373ec4",
51+
'circle-radius': 4,
52+
}
53+
},
54+
],
55+
};
18556
}
18657

187-
async function getAllCoords(): Promise<Array<{ latitude: number, longitude: number }>> {
188-
const allCoords: Array<{ latitude: number, longitude: number }> = [];
189-
const pageSize = 10000;
190-
let offset = 0;
191-
let hasMore = true;
192-
193-
while (hasMore) {
194-
const result = await fetch(`${matomoApiURL}?${new URLSearchParams({
195-
date: '2025-07-01,today',
196-
period: 'range',
197-
filter_limit: pageSize.toString(),
198-
filter_offset: offset.toString(),
199-
showColumns: 'latitude,longitude',
200-
doNotFetchActions: 'true',
201-
module: 'API',
202-
idSite: '1',
203-
format: 'json',
204-
token_auth: matomoToken,
205-
method: 'Live.getLastVisitsDetails',
206-
})}`).then(res => res.json());
207-
208-
if (Array.isArray(result) && result.length > 0) {
209-
allCoords.push(...result);
210-
offset += pageSize;
211-
if (result.length < pageSize) {
212-
hasMore = false;
213-
}
214-
} else {
215-
hasMore = false;
58+
function allStyle() {
59+
return {
60+
sources: {
61+
all: {
62+
type: "vector",
63+
tiles: ["http://localhost:8000/usage-stats/rockd/{z}/{x}/{y}"],
64+
}
65+
},
66+
layers: [
67+
{
68+
id: 'all-points',
69+
type: 'circle',
70+
source: 'all',
71+
"source-layer": "default",
72+
paint: {
73+
'circle-color': "#838383",
74+
'circle-radius': 4,
21675
}
217-
}
218-
219-
return allCoords;
76+
},
77+
],
78+
};
22079
}
22180

22281

223-
function getTodayCoords(): Array<{ latitude: number, longitude: number }> | undefined {
224-
return useAPIResult(matomoApiURL, {
225-
date: 'today',
226-
period: 'day',
227-
filter_limit: 10000,
228-
filter_offset: 0,
229-
module: 'API',
230-
format: 'json',
231-
showColumns: 'latitude,longitude',
232-
doNotFetchActions: true,
233-
idSite: '1',
234-
method: 'Live.getLastVisitsDetails',
235-
token_auth: matomoToken
236-
})
237-
}
82+
function HeatMap({
83+
mapboxToken,
84+
}: {
85+
headerElement?: React.ReactElement;
86+
title?: string;
87+
children?: React.ReactNode;
88+
mapboxToken?: string;
89+
}) {
90+
91+
const style = useMapStyle();
92+
if(style == null) return null;
93+
94+
const mapPosition = {
95+
camera: {
96+
lat: 39,
97+
lng: -98,
98+
altitude: 6000000,
99+
},
100+
};
238101

239-
function getVisitsToday(): { visits: number, visitors: number } | undefined {
240-
return useAPIResult(matomoApiURL, {
241-
method: "Live.getCounters",
242-
lastMinutes: 1440,
243-
module: 'API',
244-
format: 'json',
245-
idSite: '1',
246-
token_auth: matomoToken
247-
})?.[0]
102+
return h(
103+
"div.map-container",
104+
[
105+
// The Map Area Container
106+
h(
107+
MapAreaContainer,
108+
{
109+
className: 'map-area-container',
110+
},
111+
[
112+
h(MapView, { style, mapboxToken: mapboxAccessToken, mapPosition }),
113+
]
114+
),
115+
]
116+
);
248117
}
249118

250-
async function fetchAllUsageStats() {
251-
let allData = [];
252-
let lastId = 0;
253-
const limit = 10000;
254-
let keepFetching = true;
255-
256-
while (keepFetching) {
257-
const url = `http://localhost:5500/usage-stats?id=${lastId}&limit=${limit}`;
258-
const response = await fetch(url);
259-
if (!response.ok) {
260-
throw new Error(`Failed to fetch: ${response.statusText}`);
261-
}
119+
function useMapStyle() {
120+
const dark = useDarkMode();
121+
const isEnabled = dark?.isEnabled;
262122

263-
const json = await response.json();
264-
const batch = json.data || [];
123+
const baseStyle = isEnabled
124+
? "mapbox://styles/mapbox/dark-v10"
125+
: "mapbox://styles/mapbox/light-v10";
265126

266-
allData = allData.concat(batch);
127+
const [actualStyle, setActualStyle] = useState(null);
128+
const overlayStyle = mergeStyles(allStyle(), todayStyle()); // OVERLAY
267129

268-
if (batch.length < limit) {
269-
// Less than limit means no more data
270-
keepFetching = false;
271-
} else {
272-
// Update lastId to the max id from this batch
273-
lastId = batch.reduce((max, item) => (item.id > max ? item.id : max), lastId);
274-
}
275-
}
130+
// Auto select sample type
131+
useEffect(() => {
132+
buildInspectorStyle(baseStyle, overlayStyle, {
133+
mapboxToken: mapboxAccessToken,
134+
inDarkMode: isEnabled,
135+
}).then((s) => {
136+
setActualStyle(s);
137+
});
138+
}, [isEnabled]);
276139

277-
return allData;
140+
return actualStyle;
278141
}

0 commit comments

Comments
 (0)