wip: partial TypeScript fixes for open-mapping module

Progress on task-040 (Open-Mapping Production Ready):
- Added GeohashPrecision re-export and GeohashCommitment type alias
- Added convenience aliases for geohash functions (encodeGeohash, etc.)
- Added vector operation aliases in conics/geometry.ts
- Added combineCones, sliceConeWithPlane, angleFromAxis functions
- Fixed type annotations in RoutingService and OptimizationService
- Added sourceConstraints property to PossibilityCone
- Suppressed unused parameter warnings in stub components
- Re-enabled open-mapping in tsconfig (with exclusions for broken files)

Remaining work (~51 errors):
- Fix discovery module (MyceliumNetwork missing methods)
- Fix CollaborativeMap and MapCanvas coordinate types
- Fix remaining unused parameters in optimization.ts, useCollaboration.ts
- Fix presence manager function signatures

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2025-12-05 14:12:10 -08:00
parent af6666bf72
commit f440c4d5e1
11 changed files with 192 additions and 41 deletions

View File

@ -24,12 +24,18 @@ interface LayerPanelProps {
export function LayerPanel({ export function LayerPanel({
layers, layers,
onLayerToggle, onLayerToggle,
onLayerOpacity, onLayerOpacity: _onLayerOpacity,
onLayerReorder, onLayerReorder: _onLayerReorder,
onLayerAdd, onLayerAdd: _onLayerAdd,
onLayerRemove, onLayerRemove: _onLayerRemove,
onLayerEdit, onLayerEdit: _onLayerEdit,
}: LayerPanelProps) { }: LayerPanelProps) {
// Suppress unused variable warnings for future implementation
void _onLayerOpacity;
void _onLayerReorder;
void _onLayerAdd;
void _onLayerRemove;
void _onLayerEdit;
// TODO: Implement layer panel UI // TODO: Implement layer panel UI
// This will be implemented in Phase 2 // This will be implemented in Phase 2

View File

@ -34,16 +34,25 @@ const DEFAULT_PROFILE_COLORS: Record<RoutingProfile, string> = {
}; };
export function RouteLayer({ export function RouteLayer({
routes, routes: _routes,
selectedRouteId, selectedRouteId: _selectedRouteId,
showAlternatives = true, showAlternatives: _showAlternatives = true,
showElevation = false, showElevation: _showElevation = false,
onRouteSelect, onRouteSelect: _onRouteSelect,
onRouteEdit, onRouteEdit: _onRouteEdit,
profileColors = {}, profileColors = {},
}: RouteLayerProps) { }: RouteLayerProps) {
const colors = { ...DEFAULT_PROFILE_COLORS, ...profileColors }; const colors = { ...DEFAULT_PROFILE_COLORS, ...profileColors };
// Suppress unused variable warnings for future implementation
void _routes;
void _selectedRouteId;
void _showAlternatives;
void _showElevation;
void _onRouteSelect;
void _onRouteEdit;
void colors;
// TODO: Implement route rendering with MapLibre GL JS // TODO: Implement route rendering with MapLibre GL JS
// This will be implemented in Phase 2 // This will be implemented in Phase 2

View File

@ -23,20 +23,10 @@ interface WaypointMarkerProps {
onDelete?: (waypointId: string) => void; onDelete?: (waypointId: string) => void;
} }
export function WaypointMarker({ export function WaypointMarker(_props: WaypointMarkerProps) {
waypoint,
index,
isSelected = false,
isDraggable = true,
showLabel = true,
showTime = false,
showBudget = false,
onSelect,
onDragEnd,
onDelete,
}: WaypointMarkerProps) {
// TODO: Implement marker rendering with MapLibre GL JS // TODO: Implement marker rendering with MapLibre GL JS
// This will be implemented in Phase 1 // Props will be used in Phase 1 implementation
void _props;
return null; // Markers are rendered directly on the map return null; // Markers are rendered directly on the map
} }

View File

@ -636,3 +636,81 @@ function findOrthogonalVector(v: SpaceVector): SpaceVector {
return normalize(result); return normalize(result);
} }
// =============================================================================
// Convenience Aliases (for backwards compatibility with index.ts exports)
// =============================================================================
export const vectorAdd = addVectors;
export const vectorSubtract = subtractVectors;
export const vectorScale = scaleVector;
export const vectorDot = dotProduct;
export const vectorNorm = magnitude;
export const vectorNormalize = normalize;
export const vectorCross3D = crossProduct;
/**
* Calculate angle from cone axis to a point
*/
export function angleFromAxis(point: SpacePoint, cone: PossibilityCone): number {
const toPoint = subtractVectors(pointToVector(point), pointToVector(cone.apex));
const toPointNorm = normalize(toPoint);
const dot = dotProduct(toPointNorm, cone.axis);
return Math.acos(Math.max(-1, Math.min(1, dot)));
}
/**
* Combine two cones (union/intersection)
*/
export function combineCones(
cone1: PossibilityCone,
cone2: PossibilityCone,
operation: 'union' | 'intersection' = 'intersection'
): PossibilityCone {
// For intersection, take the narrower aperture
// For union, take the wider aperture
const aperture = operation === 'intersection'
? Math.min(cone1.aperture, cone2.aperture)
: Math.max(cone1.aperture, cone2.aperture);
// Average the apex positions
const apex: SpacePoint = {
coordinates: cone1.apex.coordinates.map((c, i) =>
(c + (cone2.apex.coordinates[i] ?? 0)) / 2
),
};
// Average the axes (normalized)
const avgAxis = normalize(addVectors(cone1.axis, cone2.axis));
return {
id: `${cone1.id}-${cone2.id}-${operation}`,
apex,
axis: avgAxis,
aperture,
direction: cone1.direction,
extent: cone1.extent && cone2.extent
? Math.min(cone1.extent, cone2.extent)
: cone1.extent ?? cone2.extent,
constraints: [...cone1.constraints, ...cone2.constraints],
sourceConstraints: [
...(cone1.sourceConstraints ?? []),
...(cone2.sourceConstraints ?? []),
],
metadata: { ...cone1.metadata, ...cone2.metadata },
};
}
/**
* Slice a cone with a hyperplane to get a conic section
*/
export function sliceConeWithPlane(
cone: PossibilityCone,
planeNormal: SpaceVector,
planePoint: SpacePoint
): ConicSection {
// Calculate plane offset as distance from origin along normal
const planeOffset = dotProduct(pointToVector(planePoint), planeNormal);
return createConicSection(cone, planeNormal, planeOffset);
}

View File

@ -745,3 +745,13 @@ export function createPathOptimizer(
): PathOptimizer { ): PathOptimizer {
return new PathOptimizer(bounds, config); return new PathOptimizer(bounds, config);
} }
// Re-export config types from types.ts for convenience
export { DEFAULT_OPTIMIZATION_CONFIG } from './types';
export type { OptimizationConfig } from './types';
/**
* Alias for backwards compatibility with index.ts
*/
export const DEFAULT_OPTIMIZER_CONFIG = DEFAULT_OPTIMIZATION_CONFIG;
export type OptimizerConfig = OptimizationConfig;

View File

@ -100,6 +100,9 @@ export interface PossibilityCone {
/** Constraints that shaped this cone */ /** Constraints that shaped this cone */
constraints: string[]; constraints: string[];
/** Source constraints (for combined cones) */
sourceConstraints?: string[];
/** Metadata */ /** Metadata */
metadata: Record<string, unknown>; metadata: Record<string, unknown>;
} }

View File

@ -427,3 +427,16 @@ export function precisionForRadius(radiusMeters: number): number {
} }
return 1; return 1;
} }
// =============================================================================
// Convenience Aliases (for backwards compatibility)
// =============================================================================
/** Alias for encode() */
export const encodeGeohash = encode;
/** Alias for decode() */
export const decodeGeohash = decode;
/** Alias for decodeBounds() */
export const getGeohashBounds = decodeBounds;

View File

@ -4,6 +4,8 @@
* Types for privacy-preserving location sharing protocol * Types for privacy-preserving location sharing protocol
*/ */
// Re-export GeohashPrecision so consumers can import from types
export type { GeohashPrecision } from './geohash';
import type { GeohashPrecision } from './geohash'; import type { GeohashPrecision } from './geohash';
// ============================================================================= // =============================================================================
@ -49,6 +51,9 @@ export interface LocationCommitment {
/** Optional: the geohash prefix that is publicly revealed */ /** Optional: the geohash prefix that is publicly revealed */
revealedPrefix?: string; revealedPrefix?: string;
/** The geohash string (at the given precision) */
geohash?: string;
} }
/** /**
@ -72,6 +77,11 @@ export interface SignedCommitment extends LocationCommitment {
signerPublicKey: string; signerPublicKey: string;
} }
/**
* Alias for LocationCommitment (used by discovery module)
*/
export type GeohashCommitment = LocationCommitment;
// ============================================================================= // =============================================================================
// Trust Circle Types // Trust Circle Types
// ============================================================================= // =============================================================================

View File

@ -4,6 +4,16 @@
import type { Waypoint, Coordinate, OptimizationServiceConfig } from '../types'; import type { Waypoint, Coordinate, OptimizationServiceConfig } from '../types';
// VROOM API response type
interface VROOMResponse {
code: number;
error?: string;
summary: { distance: number; duration: number };
routes: Array<{
steps: Array<{ type: string; job?: number }>;
}>;
}
export interface OptimizationResult { export interface OptimizationResult {
orderedWaypoints: Waypoint[]; orderedWaypoints: Waypoint[];
totalDistance: number; totalDistance: number;
@ -50,10 +60,10 @@ export class OptimizationService {
const vehicles = [{ id: 0, start: [waypoints[0].coordinate.lng, waypoints[0].coordinate.lat] }]; const vehicles = [{ id: 0, start: [waypoints[0].coordinate.lng, waypoints[0].coordinate.lat] }];
try { try {
const res = await fetch(this.config.baseUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jobs, vehicles }) }); const res = await fetch(this.config.baseUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ jobs, vehicles }) });
const data = await res.json(); const data = await res.json() as VROOMResponse;
if (data.code !== 0) throw new Error(data.error); if (data.code !== 0) throw new Error(data.error ?? 'Unknown VROOM error');
const indices = data.routes[0].steps.filter((s: any) => s.type === 'job').map((s: any) => s.job); const indices = data.routes[0].steps.filter((s) => s.type === 'job').map((s) => s.job!);
return { orderedWaypoints: indices.map((i: number) => waypoints[i]), totalDistance: data.summary.distance, totalDuration: data.summary.duration, estimatedCost: this.estimateCosts(data.summary.distance, data.summary.duration) }; return { orderedWaypoints: indices.map((i) => waypoints[i]), totalDistance: data.summary.distance, totalDuration: data.summary.duration, estimatedCost: this.estimateCosts(data.summary.distance, data.summary.duration) };
} catch { return this.nearestNeighbor(waypoints); } } catch { return this.nearestNeighbor(waypoints); }
} }

View File

@ -5,6 +5,26 @@
import type { Waypoint, Route, RoutingOptions, RoutingServiceConfig, Coordinate, RoutingProfile } from '../types'; import type { Waypoint, Route, RoutingOptions, RoutingServiceConfig, Coordinate, RoutingProfile } from '../types';
// Response types for routing APIs
interface OSRMResponse {
code: string;
message?: string;
routes: Array<{
distance: number;
duration: number;
geometry: GeoJSON.LineString;
legs: Array<{ distance: number; duration: number }>;
}>;
}
interface ValhallaResponse {
error?: string;
trip: {
summary: { length: number; time: number };
legs: Array<{ summary: { length: number; time: number } }>;
};
}
export class RoutingService { export class RoutingService {
private config: RoutingServiceConfig; private config: RoutingServiceConfig;
@ -34,9 +54,9 @@ export class RoutingService {
const url = `${this.config.baseUrl}/trip/v1/driving/${coords}?roundtrip=false&source=first&destination=last`; const url = `${this.config.baseUrl}/trip/v1/driving/${coords}?roundtrip=false&source=first&destination=last`;
try { try {
const res = await fetch(url); const res = await fetch(url);
const data = await res.json(); const data = await res.json() as { code: string; waypoints?: Array<{ waypoint_index: number }> };
if (data.code !== 'Ok') return waypoints; if (data.code !== 'Ok' || !data.waypoints) return waypoints;
return data.waypoints.map((wp: { waypoint_index: number }) => waypoints[wp.waypoint_index]); return data.waypoints.map((wp) => waypoints[wp.waypoint_index]);
} catch { return waypoints; } } catch { return waypoints; }
} }
@ -56,8 +76,8 @@ export class RoutingService {
url.searchParams.set('steps', 'true'); url.searchParams.set('steps', 'true');
if (options?.alternatives) url.searchParams.set('alternatives', 'true'); if (options?.alternatives) url.searchParams.set('alternatives', 'true');
const res = await fetch(url.toString()); const res = await fetch(url.toString());
const data = await res.json(); const data = await res.json() as OSRMResponse;
if (data.code !== 'Ok') throw new Error(`OSRM error: ${data.message || data.code}`); if (data.code !== 'Ok') throw new Error(`OSRM error: ${data.message ?? data.code}`);
return this.parseOSRMResponse(data, profile); return this.parseOSRMResponse(data, profile);
} }
@ -65,27 +85,27 @@ export class RoutingService {
const costing = profile === 'bicycle' ? 'bicycle' : profile === 'foot' ? 'pedestrian' : 'auto'; const costing = profile === 'bicycle' ? 'bicycle' : profile === 'foot' ? 'pedestrian' : 'auto';
const body = { locations: coords.map((c) => ({ lat: c.lat, lon: c.lng })), costing, alternates: options?.alternatives ?? 0 }; const body = { locations: coords.map((c) => ({ lat: c.lat, lon: c.lng })), costing, alternates: options?.alternatives ?? 0 };
const res = await fetch(`${this.config.baseUrl}/route`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); const res = await fetch(`${this.config.baseUrl}/route`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
const data = await res.json(); const data = await res.json() as ValhallaResponse;
if (data.error) throw new Error(`Valhalla error: ${data.error}`); if (data.error) throw new Error(`Valhalla error: ${data.error}`);
return this.parseValhallaResponse(data, profile); return this.parseValhallaResponse(data, profile);
} }
private parseOSRMResponse(data: any, profile: RoutingProfile): Route { private parseOSRMResponse(data: OSRMResponse, profile: RoutingProfile): Route {
const r = data.routes[0]; const r = data.routes[0];
return { return {
id: `route-${Date.now()}`, waypoints: [], geometry: r.geometry, profile, id: `route-${Date.now()}`, waypoints: [], geometry: r.geometry, profile,
summary: { distance: r.distance, duration: r.duration }, summary: { distance: r.distance, duration: r.duration },
legs: r.legs.map((leg: any, i: number) => ({ startWaypoint: `wp-${i}`, endWaypoint: `wp-${i + 1}`, distance: leg.distance, duration: leg.duration, geometry: { type: 'LineString', coordinates: [] } })), legs: r.legs.map((leg, i) => ({ startWaypoint: `wp-${i}`, endWaypoint: `wp-${i + 1}`, distance: leg.distance, duration: leg.duration, geometry: { type: 'LineString' as const, coordinates: [] } })),
alternatives: data.routes.slice(1).map((alt: any) => this.parseOSRMResponse({ routes: [alt] }, profile)), alternatives: data.routes.slice(1).map((alt) => this.parseOSRMResponse({ code: 'Ok', routes: [alt] }, profile)),
}; };
} }
private parseValhallaResponse(data: any, profile: RoutingProfile): Route { private parseValhallaResponse(data: ValhallaResponse, profile: RoutingProfile): Route {
const trip = data.trip; const trip = data.trip;
return { return {
id: `route-${Date.now()}`, waypoints: [], geometry: { type: 'LineString', coordinates: [] }, profile, id: `route-${Date.now()}`, waypoints: [], geometry: { type: 'LineString' as const, coordinates: [] }, profile,
summary: { distance: trip.summary.length * 1000, duration: trip.summary.time }, summary: { distance: trip.summary.length * 1000, duration: trip.summary.time },
legs: trip.legs.map((leg: any, i: number) => ({ startWaypoint: `wp-${i}`, endWaypoint: `wp-${i + 1}`, distance: leg.summary.length * 1000, duration: leg.summary.time, geometry: { type: 'LineString', coordinates: [] } })), legs: trip.legs.map((leg, i) => ({ startWaypoint: `wp-${i}`, endWaypoint: `wp-${i + 1}`, distance: leg.summary.length * 1000, duration: leg.summary.time, geometry: { type: 'LineString' as const, coordinates: [] } })),
}; };
} }
} }

View File

@ -27,7 +27,9 @@
}, },
"include": ["src", "worker", "src/client"], "include": ["src", "worker", "src/client"],
"exclude": [ "exclude": [
"src/open-mapping/**" "src/open-mapping/discovery/**",
"src/open-mapping/components/CollaborativeMap.tsx",
"src/open-mapping/components/MapCanvas.tsx"
], ],
"references": [{ "path": "./tsconfig.node.json" }] "references": [{ "path": "./tsconfig.node.json" }]
} }