GPS to Places¶
Turn raw GPS traces into meaningful places with familiarity scores.
Overview¶
Most location data comes as timestamped coordinates—GPS traces from phones, fitness trackers, or vehicle telemetry. Chora transforms these raw points into meaningful places by detecting where people dwell, building familiarity over repeated visits, and creating a graph of platial relationships.
graph LR
A[GPS Points] --> B[Encounters]
B --> C[Dwell Detection]
C --> D[Spatial Extents]
D --> E[Familiarity Scores]
E --> F[Emergent Places]
Step 1: Load GPS Data¶
From GPX Files¶
import gpxpy
from chora.core import PlatialGraph, Agent, SpatialExtent, Encounter, PlatialEdge
from datetime import datetime
# Create graph and agent
graph = PlatialGraph(name="my_walks")
alice = Agent.individual("alice")
graph.add_node(alice)
# Parse GPX
with open("morning_walk.gpx") as f:
gpx = gpxpy.parse(f)
for track in gpx.tracks:
for segment in track.segments:
for point in segment.points:
# Create spatial extent for this point
extent = SpatialExtent.from_point(
lon=point.longitude,
lat=point.latitude,
name=f"pt_{point.time.isoformat()}"
)
graph.add_node(extent)
# Create encounter
encounter = Encounter(
agent_id=alice.id,
extent_id=extent.id,
start_time=point.time,
activity="walking"
)
graph.add_node(encounter)
# Link agent → encounter → extent
graph.add_edge(PlatialEdge.participates_in(alice.id, encounter.id))
graph.add_edge(PlatialEdge.occurs_at(encounter.id, extent.id))
Using the CLI¶
Step 2: Cluster Into Dwells¶
Raw GPS points are noisy. We need to cluster nearby points into dwells—locations where someone stayed for a meaningful duration.
from chora.streaming import StreamProcessor, StreamConfig, LocationEvent
# Configure dwell detection
config = StreamConfig(
dwell_radius_m=50.0, # Points within 50m are same place
dwell_time_s=60.0, # 60 seconds to trigger a dwell
min_dwell_for_encounter=120.0 # 2 min to create encounter
)
processor = StreamProcessor(graph, config)
# Process locations
for point in gps_points:
event = LocationEvent(
agent_id="alice",
longitude=point.lon,
latitude=point.lat,
timestamp=point.time
)
events = processor.process(event)
for e in events:
if e.event_type == "encounter":
print(f"Dwell detected at {e.data['location']}")
Step 3: Derive Familiarity¶
Familiarity grows with repeated visits and decays over time without reinforcement.
from chora.derive import update_familiarity
from chora.core.types import NodeType
# Update familiarity for all encounters
for encounter in graph.nodes(NodeType.ENCOUNTER):
update_familiarity(graph, encounter)
# Check familiarity scores
from chora.query import find_familiar_places, AgentId
familiar = find_familiar_places(
graph,
AgentId(str(alice.id)),
min_familiarity=0.5
)
for place in familiar:
print(f"{place.extent.name}: {place.familiarity:.2f}")
Output:
Step 4: Extract Emergent Places¶
Places emerge from patterns of encounters—they're not predefined, they're discovered.
from chora.derive.place import extract_place
# Extract place for a spatial extent based on alice's encounters
place = extract_place(graph, extent_id=park.id, agent_id=alice.id)
print(f"Place: {place.name}")
print(f"Visit count: {place.encounter_count}")
print(f"Familiarity: {place.familiarity}")
print(f"Character: {place.semantic_hints}")
Complete Example¶
"""Full GPS-to-Places pipeline."""
from chora.core import PlatialGraph, Agent, SpatialExtent, Encounter, PlatialEdge
from chora.derive import update_familiarity
from chora.query import find_familiar_places
from chora.streaming import create_processor
import gpxpy
def process_gpx(gpx_path: str, agent_name: str = "user"):
# Setup
graph = PlatialGraph(name="locations")
agent = Agent.individual(agent_name)
graph.add_node(agent)
# Create streaming processor for dwell detection
processor = create_processor(
graph,
dwell_radius=50.0, # meters
dwell_time=60.0 # seconds
)
# Load and process GPS
with open(gpx_path) as f:
gpx = gpxpy.parse(f)
for track in gpx.tracks:
for segment in track.segments:
for point in segment.points:
from chora.streaming import LocationEvent
event = LocationEvent(
agent_id=agent_name,
longitude=point.longitude,
latitude=point.latitude,
timestamp=point.time
)
processor.process(event)
# Derive familiarity
from chora.core.types import NodeType
for enc in graph.nodes(NodeType.ENCOUNTER):
update_familiarity(graph, enc)
# Query results
from chora.core.types import AgentId
places = find_familiar_places(graph, AgentId(agent_name), min_familiarity=0.3)
return graph, places
# Run
graph, places = process_gpx("my_track.gpx", "alice")
for p in places:
print(f"{p.extent.name}: {p.familiarity:.2f}")
Next Steps¶
- Affective Mapping — Add emotional qualities
- Practice Detection — Find routines in your data
- CLI Reference — Full CLI documentation