Lahug research geographic projection, version 1 This package retains all 1679 geographic dependency structures (1420 nodes,254 ways,5 relations). The private source responses and account/contact metadata are not republished. Exact input/query/receipt SHA-256 pins and source clocks are in lahug-research.json and manifest.json. Requested object as-of, response provider database time, capture and per-object map edit are distinct; none certifies current conditions. Old v2 and CommuteTour offers are unchanged. Projection rules and per-key dropped counts are encoded in the JSON projection field. Native element order, numeric coordinates, way-node sequences and relation-member order/roles are preserved. Retained tag strings are literal. No geometry is clipped to discovery circles, joined, reversed or snapped. The display admits only two private footways and barrier/constraint context. Crossings, restrictions and grade tags are not evaluated for permission; all nine captured platforms have zero acquired-way attachment. There is no walking graph admission or facility/entrance/CommuteTour join. Deterministic method (JavaScript; no dependency or network request): const retainedObjectKeys = ["type","id","version","timestamp","lat","lon","nodes","members","tags"]; const retainedTagKeys = ["access","access:conditional","area","barrier","bicycle","bridge","bus","covered","crossing","crossing:markings","crossing_ref","direction","foot","foot:conditional","ford","highway","incline","indoor","junction","kerb","lane_markings","lanes","lanes:backward","lanes:forward","layer","level","lit","maxspeed","maxspeed:type","oneway","public_transport","restriction","restriction:conditional","service","smoothness","source","source:geometry","surface","tactile_paving","traffic_calming","turn:lanes:backward","turn:lanes:forward","tunnel","type","waterway","wheelchair"]; const seedNodeIds = [13760438081,10251749630,13760438074]; function projectGeography(elements) { const droppedObjectKeys = {}; const droppedTagKeys = {}; const objects = elements.map(element => { const object = {}; for (const [key,value] of Object.entries(element)) { if (!retainedObjectKeys.includes(key)) { droppedObjectKeys[key] = (droppedObjectKeys[key] ?? 0) + 1; continue; } if (key !== 'tags') { object[key] = value; continue; } const tags = {}; for (const [tag,tagValue] of Object.entries(value)) { if (retainedTagKeys.includes(tag)) tags[tag] = tagValue; else droppedTagKeys[tag] = (droppedTagKeys[tag] ?? 0) + 1; } object.tags = tags; } return object; }); return { objects, droppedObjectKeys, droppedTagKeys }; } function deriveDisplayDataset(objects, sourceId = 'LHN-A01') { const nodes = new Map(objects.filter(object => object.type === 'node').map(object => [object.id,object])); const ways = objects.filter(object => object.type === 'way'); const memberships = id => ways.filter(way => way.nodes.includes(id)).map(way => way.id); const displayObject = object => ({ type:object.type,id:object.id,version:object.version,editedAt:object.timestamp,tags:object.tags ?? {},coordinates:object.type === 'node' ? [object.lat,object.lon] : object.nodes.map(id => { const node=nodes.get(id); if (!node) throw new Error(`Missing node ${id}`); return [node.lat,node.lon]; }),...(object.type === 'way' ? {nodeIds:object.nodes,nodeTags:object.nodes.map(id => nodes.get(id).tags ?? {})} : {}) }); const privateLines = ways.filter(way => way.tags?.highway === 'footway'); if (privateLines.length !== 2 || !privateLines.every(way => way.tags.access === 'private')) throw new Error('Private-footway source contract changed'); const barriers = objects.filter(object => object.tags?.barrier); const crossingObjects = objects.filter(object => object.type === 'node' && object.tags?.highway === 'crossing').map(object => ({...displayObject(object),incidentWayIds:memberships(object.id)})); const restrictions = objects.filter(object => object.type === 'relation').map(object => ({type:object.type,id:object.id,version:object.version,editedAt:object.timestamp,tags:object.tags ?? {},members:object.members})); const gradeObjects = ways.filter(way => way.tags?.bridge || way.tags?.layer || way.tags?.level || way.tags?.incline).map(object => ({type:object.type,id:object.id,version:object.version,editedAt:object.timestamp,tags:object.tags ?? {}})); return { sourceId,coordinateOrder:'latitude,longitude',lines:[],privateLines:privateLines.map(displayObject),barriers:barriers.map(displayObject),constraints:{conditionalObjects:[],crossingObjects,restrictions,gradeObjects,seedGaps:seedNodeIds.map(id => ({nodeId:id,capturedWayIds:memberships(id),endpointAttachmentStatus:'unresolved'}))},endpointConnectorCount:0,admittedWalkingJourneyCount:0,publicAccessStatus:'unverified',stepFreeStatus:'unverified' }; } Reconstruct the shipped displayDataset from offered data in Node.js: const {readFileSync} = await import('node:fs'); const offer = JSON.parse(readFileSync('lahug-research.json','utf8')); const rebuilt = deriveDisplayDataset(offer.geographicObjects,offer.source.id); if (JSON.stringify(rebuilt) !== JSON.stringify(offer.displayDataset)) throw new Error('Display mismatch'); Projection can be independently audited with projectGeography(original.elements) against the privately pinned original. Reconstruction of every displayed feature requires only the public geographicObjects, not private account/contact metadata. Runtime JSON is JSON.stringify(displayDataset) + newline; manifest pins its exact bytes.