Use real H7 centroids from API — remove spiral approximation
This commit is contained in:
@@ -2,11 +2,18 @@ import { useState, useEffect } from 'react'
|
||||
import { WAYPOINTS, ROUTES } from '../constants.js'
|
||||
import { fetchMapCells } from '../api.js'
|
||||
|
||||
// Bounding box — Mediterranean basin
|
||||
const BBOX = { minLng: 5, maxLng: 38, minLat: 28, maxLat: 48 }
|
||||
const W = 800
|
||||
const H = 460
|
||||
|
||||
const H5_IDS = {
|
||||
ostia: '851e805bfffffff',
|
||||
capua: '851e8333fffffff',
|
||||
brundisium: '851e8ba3fffffff',
|
||||
carthago: '85386e23fffffff',
|
||||
alexandria: '853f5ba7fffffff',
|
||||
}
|
||||
|
||||
// H3 res-5 cell centre coordinates — hardcoded, h3-js not in browser bundle
|
||||
const CENTRES = {
|
||||
ostia: { lat: 41.73, lng: 12.23 },
|
||||
@@ -16,62 +23,29 @@ const CENTRES = {
|
||||
alexandria: { lat: 31.20, lng: 29.92 },
|
||||
}
|
||||
|
||||
// H3 res-5 hex string IDs — used for API calls
|
||||
const H5_IDS = {
|
||||
ostia: '851e805bfffffff',
|
||||
capua: '851e8333fffffff',
|
||||
brundisium: '851e8ba3fffffff',
|
||||
carthago: '85386e23fffffff',
|
||||
alexandria: '853f5ba7fffffff',
|
||||
}
|
||||
|
||||
const WAYPOINT_IDS = ['ostia', 'capua', 'brundisium', 'carthago', 'alexandria']
|
||||
const ROUTE_ORDER = ['olive', 'wine', 'grain', 'linen']
|
||||
|
||||
// Active epoch — Roman period for OTIVM-II
|
||||
// sl_offset_cm = -10 (effectively zero). Coastline matches modern sea level.
|
||||
const EPOCH = 'roman_14bce'
|
||||
|
||||
// H7 cell approximate radius in SVG pixels — H7 inradius ~10km
|
||||
// At our projection (33° = 800px wide), 1° lng ≈ 24px, 10km ≈ 9px
|
||||
const H7_RADIUS = 9
|
||||
|
||||
function project(lat, lng) {
|
||||
const x = ((lng - BBOX.minLng) / (BBOX.maxLng - BBOX.minLng)) * W
|
||||
const y = ((BBOX.maxLat - lat) / (BBOX.maxLat - BBOX.minLat)) * H
|
||||
return { x, y }
|
||||
}
|
||||
|
||||
// Approximate H7 cell position within its H5 parent using a sunflower spiral.
|
||||
// H7 cells are rendered as circles — exact hex geometry requires h3-js (server-side only).
|
||||
// The spiral distributes 49 cells across the H5 area. Visual approximation only.
|
||||
// Will be replaced when the API returns per-cell centroids.
|
||||
function h7ApproxPos(h5Id, cellIndex) {
|
||||
const cx = CENTRES[h5Id]
|
||||
if (!cx) return null
|
||||
const { x: cx_px, y: cy_px } = project(cx.lat, cx.lng)
|
||||
// Golden angle spiral — distributes points evenly across H5 area (~90km)
|
||||
// At our projection scale, H5 spans ~120px. Space cells ~16px apart.
|
||||
const SPACING = 14
|
||||
const angle = cellIndex * 2.399963 // golden angle radians
|
||||
const radius = SPACING * Math.sqrt(cellIndex)
|
||||
return {
|
||||
x: cx_px + Math.cos(angle) * radius,
|
||||
y: cy_px + Math.sin(angle) * radius,
|
||||
}
|
||||
}
|
||||
|
||||
const H7_RADIUS = 8
|
||||
|
||||
export default function Map({ state }) {
|
||||
const chapter = state?.chapter ?? 1
|
||||
const [cellData, setCellData] = useState({})
|
||||
|
||||
// Fetch H7 cells for all revealed waypoints.
|
||||
// A waypoint is revealed when chapter >= waypoint.chapter.
|
||||
// Ostia (chapter 1) is revealed from the start.
|
||||
// Cells are fetched once per waypoint and cached in state.
|
||||
useEffect(() => {
|
||||
WAYPOINT_IDS.forEach((id) => {
|
||||
const wp = WAYPOINTS[id]
|
||||
if (chapter < wp.chapter) return // not yet revealed
|
||||
if (cellData[id]) return // already loaded
|
||||
if (chapter < wp.chapter) return
|
||||
if (cellData[id]) return
|
||||
fetchMapCells(H5_IDS[id], EPOCH).then(data => {
|
||||
if (data?.cells) {
|
||||
setCellData(prev => ({ ...prev, [id]: data.cells }))
|
||||
@@ -94,24 +68,24 @@ export default function Map({ state }) {
|
||||
style={{ width: '100%', maxWidth: W, display: 'block', margin: '0 auto' }}
|
||||
aria-label="Mediterranean trade map"
|
||||
>
|
||||
{/* Sea — permanent darkness beyond fog of war */}
|
||||
{/* Sea — permanent darkness */}
|
||||
<rect width={W} height={H} fill="#0d1a26" />
|
||||
|
||||
{/* Fog of war — land H7 cells, revealed by chapter */}
|
||||
{/* Fog of war — land H7 cells at real geographic positions */}
|
||||
{WAYPOINT_IDS.map((id) => {
|
||||
const wp = WAYPOINTS[id]
|
||||
if (chapter < wp.chapter) return null
|
||||
const cells = cellData[id]
|
||||
if (!cells) return null
|
||||
return cells.map((cell, cellIdx) => {
|
||||
return cells.map((cell) => {
|
||||
if (!cell.is_land) return null
|
||||
const pos = h7ApproxPos(id, cellIdx)
|
||||
if (!pos) return null
|
||||
// lat/lon now come directly from the API — real H7 centroids
|
||||
const { x, y } = project(cell.lat, cell.lon)
|
||||
return (
|
||||
<circle
|
||||
key={`${id}-${cell.h7}`}
|
||||
cx={pos.x}
|
||||
cy={pos.y}
|
||||
cx={x}
|
||||
cy={y}
|
||||
r={H7_RADIUS}
|
||||
fill="#2d3b2a"
|
||||
stroke="#3a4a30"
|
||||
@@ -126,7 +100,6 @@ export default function Map({ state }) {
|
||||
{ROUTE_ORDER.map((routeId) => {
|
||||
const route = ROUTES.find((r) => r.id === routeId)
|
||||
if (!route) return null
|
||||
// Route visible when both endpoints are revealed
|
||||
const fromWp = WAYPOINTS[route.from]
|
||||
const toWp = WAYPOINTS[route.to]
|
||||
if (chapter < fromWp.chapter || chapter < toWp.chapter) return null
|
||||
|
||||
Reference in New Issue
Block a user