clone

an offline map explorer where each world, from a game map to a city's open data, ships as one .atlas file

main
atlas / internal / enrich / enrichers / lenses / lenses.go
lenses.go 3.4K
1// Package lenses attaches pictures to grounds.2//3// A world is a ground you can stand on; a lens is a picture of it. The two are4// separable, and that separation is what makes this an enricher rather than a5// step in composition: a ground published years ago can be given another6// picture -- a second map style, a later capture, somebody else's raster warped7// into this world's space -- without the ground itself changing, and without8// anything already published being touched. A new build lands beside the old9// one carrying one more picture.10//11// # What this enricher does and does not decide12//13// It does not derive pyramids. Deriving rasters is the generate lane's work14// (fetching is crawling, cutting is tiling), and the derivation stamp a pyramid15// carries is written there. What arrives here is the offer: these pictures were16// derived for this ground, each with the stamp that says what it was made from.17// This enricher decides which of them the world does not already have, attaches18// those, and carries the stamps through so the picture in the bundle can always19// be traced back to the derivation that produced it.20//21// An offer whose stamp differs from the lens already attached under that name is22// a re-derivation, and it updates in place: the ground has not changed and the23// picture is of the same tile set, so what moves is the stamp. An offer that24// would repoint a source's lens at another tile set is refused by the25// contribution's own rules -- that would be a rewrite of what a source said its26// picture was.27package lenses2829import (30 "log/slog"3132 "github.com/FelineStateMachine/atlas/internal/enrich"33 "github.com/FelineStateMachine/atlas/internal/logging"34)3536// Name is what curation queues this enricher as.37const Name = "lenses"3839// Enricher attaches offered pictures to the worlds they picture.40type Enricher struct{}4142// New builds the enricher.43func New() *Enricher { return &Enricher{} }4445func (*Enricher) Name() string { return Name }4647// Declares is empty: a lens is a picture, not a claim about the ground.48func (*Enricher) Declares() []string { return nil }4950// Enrich attaches every offered picture a world does not already carry.51func (e *Enricher) Enrich(v *enrich.Volume, ctx enrich.Context) (enrich.Contribution, error) {52 out := enrich.Contribution{Enricher: Name, Volume: v.Slug}53 if ctx.Lenses == nil {54 return out, nil55 }56 log := ctx.Log57 if log == nil {58 log = slog.New(slog.DiscardHandler)59 }60 log = log.With(logging.Op(Name), logging.Volume(v.Slug))6162 for _, world := range v.Worlds {63 for _, offer := range ctx.Lenses.Offers(world.Slug) {64 if offer.Name == "" || offer.TileSet == "" {65 continue66 }67 if standing, held := attached(world, offer.Name); held {68 // Same picture, same derivation: there is nothing to say, and69 // saying it would be a build with no change in it.70 if standing.TileSet == offer.TileSet && standing.Stamp == offer.Stamp &&71 standing.AlignedWith == offer.AlignedWith {72 continue73 }74 }75 attach := offer76 out.Ops = append(out.Ops, enrich.Op{77 Kind: enrich.OpSetLens, World: world.Slug, Lens: &attach,78 })79 log.Info("lens attached", logging.World(world.Slug), logging.Lens(offer.Name),80 "tileSet", offer.TileSet, "aligned", offer.AlignedWith != "")81 }82 }83 return out, nil84}8586func attached(w enrich.World, name string) (enrich.Lens, bool) {87 for _, lens := range w.Lenses {88 if lens.Name == name {89 return lens, true90 }91 }92 return enrich.Lens{}, false93}