From b2c7ab5078cf82958d6bd5b9f66281649fb47d76 Mon Sep 17 00:00:00 2001 From: fretchen Date: Sun, 23 Aug 2026 14:37:03 +0200 Subject: [PATCH 01/12] Neuer Plan --- website/blog/box-dimension-post-plan.md | 97 ++++++++ website/blog/kuesten_dimension.plan.md | 317 ++++++++++++++++++++++++ 2 files changed, 414 insertions(+) create mode 100644 website/blog/box-dimension-post-plan.md create mode 100644 website/blog/kuesten_dimension.plan.md diff --git a/website/blog/box-dimension-post-plan.md b/website/blog/box-dimension-post-plan.md new file mode 100644 index 000000000..7a42ae18e --- /dev/null +++ b/website/blog/box-dimension-post-plan.md @@ -0,0 +1,97 @@ +# Implementierungsplan: Box-Dimension-Post (Bretagne/GB) + +## Ziel + +Ein Blogpost, der Kindern (und Erwachsenen) das Konzept der Box-Counting-Dimension +zeigt: erst am einfachen Beispiel (Linie, Kreis), dann spielerisch an echten +Küsten/Seen. Zwei interaktive Bausteine, ein gemeinsamer Kern. + +## Content-Gliederung des Posts + +1. **Einstieg**: Warum ist eine Küste "länger", je genauer man misst? + (Kurzer Verweis auf Mandelbrot, "How Long Is the Coast of Britain?", 1967) +2. **Konzept-Widget**: Linie/Kreis/gezackte Kurve + Gitter-Regler + Log-Log-Plot + → Kernaussage: d = log N(ε) / log(1/ε), Linie und Kreis beide d=1 +3. **Text-Brücke**: reale Küsten sind keine glatten Kurven → d wird > 1 + - Westküste Großbritannien: D ≈ 1,25 (Mandelbrot/Richardson) + - grobe Faustregel: sandige/glatte Küsten nahe D≈1, felsige Küsten D≈1,3+ +4. **Karten-Spiel**: Bretagne (felsig) vs. Kontrastregion (glatter) vs. optional + ein See, mit Gitter-Overlay direkt auf der Karte +5. **Abschluss**: eigene Beobachtung/Ergebnis der Leser:innen einordnen + +## Architekturentscheidung + +- **Kartenbibliothek**: Leaflet.js über `react-leaflet` (Begründung: KISS, + siehe Chat — MapLibre GL wäre für diesen Zweck Overkill) +- **Tiles**: CARTO Positron (kostenlos, kein API-Key, `© OpenStreetMap contributors` + Attribution einbauen) +- **Geodaten**: GSHHG (Global Self-consistent, Hierarchical, High-resolution + Geography Database) — public domain, exakt der Datensatz aus der + akademischen Coastline-Dimension-Literatur, enthält auch Seen (Level 2) +- **Gemeinsamer Kern**: eine einzige Box-Counting-Utility (reine Funktion, + kein React), die sowohl vom Canvas-Demo-Widget als auch vom Leaflet-Overlay + genutzt wird + +## Komponentenstruktur (Vorschlag) + +``` +website/components/box-dimension/ + boxCounting.ts # reine Logik: countIntersectingCells(points, cellSizePx) + ConceptDemo.tsx # Canvas-Widget: Linie/Kreis/Fraktal + Slider + Log-Log-Plot + LogLogPlot.tsx # gemeinsame Plot-Komponente (von beiden genutzt) + CoastPlayground.tsx # Leaflet-Karte + Gitter-Overlay + Regionsauswahl + GridOverlay.ts # custom L.Layer, zeichnet Gitter via latLngToContainerPoint + regions.ts # Liste { id, label, geojsonPath, center, zoom } + +website/public/data/coastlines/ + bretagne.geojson + contrast-region.geojson # z.B. glattere Küste zum Vergleich + lake-example.geojson # optional +``` + +## Datenaufbereitung (einmalig, vor dem Coden) + +1. GSHHG herunterladen (volle Auflösung reicht als Quelle) +2. Mit `mapshaper` oder `ogr2ogr` auf Bounding Box zuschneiden: + - Bretagne (grob: 47.2–48.9°N, -5.2–-1.0°E) + - Kontrastregion (z.B. glatterer Küstenabschnitt zum Vergleich) +3. Auf vernünftige Punktdichte vereinfachen (Douglas-Peucker), damit das + Gitter-Overlay im Browser performant bleibt — nicht die volle GSHHG-Auflösung + direkt ausliefern +4. Nach GeoJSON exportieren, Dateigröße prüfen (Ziel: einige hundert KB pro Region) +5. Dateien nach `website/public/data/coastlines/` legen + +## Implementierungsschritte + +- [ ] **Schritt 1**: `boxCounting.ts` aus dem bestehenden Demo-Code extrahieren + (reine Funktion, unabhängig von Canvas/Leaflet) +- [ ] **Schritt 2**: `ConceptDemo.tsx` als React-Komponente bauen + (Canvas-Ref + useEffect statt globaler DOM-Listener, sonst 1:1 aus der Demo) +- [ ] **Schritt 3**: `LogLogPlot.tsx` als eigenständige, wiederverwendbare + Komponente auslagern +- [ ] **Schritt 4**: GSHHG-Daten besorgen, zuschneiden, vereinfachen (siehe oben) +- [ ] **Schritt 5**: `CoastPlayground.tsx` — Leaflet-Karte mit TileLayer (CARTO) + + GeoJSON-Layer für die gewählte Region +- [ ] **Schritt 6**: `GridOverlay.ts` — custom Leaflet-Layer, zeichnet Gitter neu + bei `moveend`/`zoomend`, zählt Schnittzellen, meldet N(ε) nach oben +- [ ] **Schritt 7**: Regionsauswahl (Dropdown/Buttons) + Slider für Zellgröße + (in km, intern in Pixel umgerechnet je nach Zoomstufe) +- [ ] **Schritt 8**: `CoastPlayground` an `LogLogPlot` anschließen (gleiche + Komponente wie im Concept-Widget) +- [ ] **Schritt 9**: Attribution-Zeile einbauen (`© OpenStreetMap contributors`, + GSHHG-Quellenangabe) +- [ ] **Schritt 10**: Post-Text schreiben, Widgets einbetten, gegenlesen + +## Offene Entscheidungen (vor dem Start klären) + +- Welche zweite Region als Kontrast zur Bretagne? (glattere Küste oder See) +- Soll der Slider in "km Kantenlänge" oder in "Anzahl Kästchen" gedacht werden? +- Reicht eine feste Auswahl an Regionen, oder soll perspektivisch doch eine + Overpass-Live-Abfrage ergänzt werden (spätere Ausbaustufe, nicht für v1)? + +## Lizenz-/Attributionshinweise + +- OSM-Kartendaten (über CARTO-Tiles): ODbL, Attribution `© OpenStreetMap + contributors` ist Pflicht und muss sichtbar auf der Karte stehen +- GSHHG: public domain (Quellen WVS/WDBII), Software/Daten-Distribution + unter LGPL seit Version 2.2.2 — Quellenangabe im Post trotzdem fair diff --git a/website/blog/kuesten_dimension.plan.md b/website/blog/kuesten_dimension.plan.md new file mode 100644 index 000000000..74da9bce3 --- /dev/null +++ b/website/blog/kuesten_dimension.plan.md @@ -0,0 +1,317 @@ +# Blog Post Plan: Wie lang ist die Küste der Bretagne? + +## Target Audience + +**Primär: mathematikinteressierte Kinder, etwa 10 bis 12 Jahre.** Das ist eine +*vierte* Zielgruppe, die es auf diesem Blog bisher nicht gibt — bewusst +gewählt, nicht "Kinder und Erwachsene gleichzeitig". Sekundär dürfen Erwachsene +mitlesen, aber kein einziger Satz wird für sie kalibriert. + +**Nicht persönlich.** Der Post ist für Kinder in diesem Alter allgemein +geschrieben, nicht für bestimmte Kinder. Keine Namen, keine +Familien-Anspielungen, kein "erinnert ihr euch". Ein Kind, das die Bretagne nie +gesehen hat, muss vom ersten Satz an genauso mitkommen. Die Bretagne ist das +*Beispiel*, nicht die gemeinsame Erinnerung. + +**Was sie können:** + +- Multiplizieren, Dividieren, Bruchteile, Prozente +- Flächen (Quadrat, Rechteck), Umfang +- Koordinaten, Maßstab auf einer Karte +- Zählen, Schätzen, Vergleichen — und sie machen das gerne + +**Was sie NICHT können (harte Grenze):** + +- **Logarithmen** — kommen erst in Klasse 9/10. Das ist die zentrale + Konsequenz für diesen Post: `d = log N(ε) / log(1/ε)` darf **nicht** im + Haupttext stehen. +- Potenzen mit gebrochenen Exponenten +- Steigung einer Regressionsgerade, log-log-Achsen + +**Was sie lernen sollen:** + +- Dass "Länge" bei einer zerklüfteten Küste keine feste Zahl ist, sondern vom + Messstab abhängt +- Dass man Zerklüftetheit *messen* kann — mit einer Zahl zwischen 1 und 2 +- Dass diese Zahl "Dimension" heißt und dass es Dinge gibt, die weder Linie + noch Fläche sind + +## Core Thesis + +**Wenn du deine Kästchen halb so groß machst, brauchst du bei einer geraden +Linie doppelt so viele, bei einer vollen Fläche viermal so viele — und bei +einer echten Küste irgendwas dazwischen. Genau diese Zahl dazwischen ist ihre +Dimension.** + +Warum das zählt: Dimension kennt man als 1, 2 oder 3 — Linie, Fläche, Raum. +Hier darf sie plötzlich 1,26 sein. Das ist der Moment, den der Post verkauft. + +## Der Verdopplungs-Trick (Kernentscheidung, ersetzt den Log-Log-Plot) + +Statt Steigung im Log-Log-Plot: **Halbierungsspiel.** + +| Was | Kästchen halbiert → so viel mehr gebraucht | Dimension | +|---|---|---| +| Gerade Linie | 2× | 1 | +| Kreis(-linie) | 2× | 1 | +| Volles Quadrat | 4× | 2 | +| Küste Bretagne | ca. 2,4× | ca. 1,3 | + +Die Zahlen der letzten Zeile sind **Platzhalter zur Illustration** — der +tatsächliche Wert steht erst nach Schritt 7 fest. + +Das Widget zeigt genau diesen einen Faktor als große Zahl. Der Weg von "2,4×" +zur Dimension "1,3" läuft über eine **visuelle Skala** (ein Zifferblatt/Balken +von 2 bis 4, beschriftet 1 bis 2) — kein Logarithmus, aber mathematisch +ehrlich, weil es genau `log₂` ist. + +Die Formel kommt in ein `` mit dem Titel *"Für Papa und die Großen"*. +Dort darf der Log-Log-Plot und `d = log N / log(1/ε)` stehen. Das ist der +einzige Ort im Post mit Notation. + +## Outline + +Zielumfang: **900–1200 Wörter Deutsch**, 4 Abschnitte. Nicht mehr. + +1. **Stell dir vor, du wanderst an der Küste der Bretagne** (~150 W) + Konkrete Szene, aber unpersönlich erzählt: eine Bucht, die von oben auf der + Karte winzig aussieht — und die zu Fuß ewig dauert, weil sie voller kleiner + Buchten in der Bucht steckt. Frage an den Leser: Wie lang ist die Küste + eigentlich? Antwort: Kommt drauf an, wie genau man hinschaut. Funktioniert + für jedes Kind, das die Bretagne noch nie gesehen hat — das Foto liefert + das Bild, der Text braucht keine gemeinsame Erinnerung vorauszusetzen. + *[TODO: Fred — welches Foto aus deinem Fundus zeigt eine Bucht mit + erkennbar zerklüfteter Küstenlinie? Das ist wichtiger als ein "schönes" + Bild.]* + +2. **Das Kästchenspiel** (~250 W) → **Widget 1: `KaestchenSpiel`** + Erst raten lassen, dann zählen. Gerade Linie, Kreis, volles Quadrat. + Schieberegler halbiert die Kästchen, das Widget zeigt den Faktor. + Ergebnis: Linie und Kreis → 2×, Quadrat → 4×. + +3. **Und jetzt eine echte Küste** (~300 W) → **Widget 2: `KuestenSpiel`** + Dieselbe Mechanik auf der Bretagne-Küste. Faktor liegt dazwischen. + Umschalten auf die Normandie: derselbe Regler, viel kleinerer Faktor. + Kernsatz: Die Küste ist "mehr als eine Linie, aber weniger als eine Fläche". + Einordnung: Mandelbrot hat das 1967 für Großbritannien gemacht, D ≈ 1,25. + + **Framing-Regel: "zerklüftet vs. glatt", nicht "Fels vs. Sand".** Die + Normandie-Kontrastküste ist Kreidefels und trotzdem fast gerade — eine + Material-Erklärung wäre also schlicht falsch und bricht am ersten + Gegenbeispiel. Der Post beobachtet die Form, er erklärt keine Geologie. + Spart nebenbei Wörter. + +4. **Selber malen** (~150 W) → **Widget 3 (Teil von Widget 1): Finger-Modus** + Sie zeichnen mit dem Finger auf dem iPad eine eigene Kurve und bekommen + deren Dimension. Glatter Bogen → nahe 1. Wildes Gekritzel → deutlich höher. + Das ist der Abschluss, nicht ein Fazit-Absatz. + +Plus: `` "Für Papa und die Großen" mit der Formel und dem Log-Log-Plot. + +## Interactive Elements + +**Architekturentscheidung: kein Leaflet, keine Kartentiles.** Begründung unten +unter "Mobile". Alles läuft auf *einem* Canvas-Widget mit austauschbarem Inhalt. + +**Dateiformat: `.mdx` mit importierten Komponenten**, nicht `.tsx` wie +`tragedy_of_commons_fishing.tsx`. Der Post-Text lebt als Markdown in +`kuesten_dimension.mdx`, die Widgets sind eigenständige Komponenten unter +`components/blog/`, die per `import` eingebunden werden — exakt das Muster von +`housing_risk_portfolio.mdx` (`ShockCalculator`, `RiskReality`) und +`etf_diversification_interactive.mdx`. + +``` +website/components/blog/box-dimension/ + boxCounting.ts # reine Logik: countCells(points, cellSizePx) -> N + BoxCanvas.tsx # Canvas: zeichnet Kurve + Gitter, hebt Trefferzellen hervor + KaestchenSpiel.tsx # Widget 1: Linie/Kreis/Quadrat/Finger-Modus + KuestenSpiel.tsx # Widget 2: Bretagne vs. glatte Küste, gleicher BoxCanvas + DimensionSkala.tsx # der Balken 2×…4× -> 1…2, ersetzt den Log-Log-Plot + shapes.ts # Linie, Kreis, Quadrat als Punktlisten + coasts.ts # vorverarbeitete Küstenumrisse, INLINE als Punktarrays + +website/blog/kuesten_dimension.mdx + # Frontmatter + Fließtext + vier import-Zeilen für KaestchenSpiel, + # KuestenSpiel, MyFigure, Foldable — kein export const meta, normales + # YAML-Frontmatter wie bei allen anderen .mdx-Posts +``` + +**Wiederverwendung aus dem Repo (nichts davon neu bauen):** + +- `components/Foldable.tsx` — für den Erwachsenen-Exkurs +- `components/MarkdownWithLatex.tsx` + KaTeX — für die Formel im Foldable +- `react-chartjs-2` / `chart.js` (bereits Abhängigkeit, siehe + `DiversificationRandomWalk.tsx`) — für den Log-Log-Plot im Foldable. + **Kein eigenes `LogLogPlot.tsx` bauen.** +- `components/blog/palette.ts` — Farbtokens, Panda-Regel 1 beachten +- `components/MyFigure.tsx` — Aufmacherfoto mit Bildunterschrift. Bilddatei + nach `website/public/blog/`, Referenz als `/blog/.jpg` (Muster: + `collect_imagegen.mdx`). Fürs iPhone vorher auf ~1200 px Breite runterrechnen. + +## Mobile (iPad / iPhone — Hauptplattform) + +Das war im ersten Plan gar nicht adressiert und kippt die größte Entscheidung: + +- **Leaflet fällt raus.** Eine pan-/zoombare Karte auf dem iPhone frisst + Scroll-Gesten — der Leser bleibt in der Karte hängen statt weiterzulesen. + Dazu: `moveend`/`zoomend`-getriggertes Neuzeichnen des Gitters, ein Slider + direkt neben einer schwenkbaren Karte auf 390 px Breite, plus + `leaflet` + `react-leaflet` (~150 KB) und Tile-Requests. Für null + didaktischen Mehrwert — die Kinder brauchen keine zoombare Weltkarte, sie + brauchen *einen* Küstenumriss. + Ersatz: fester Umriss auf Canvas, daneben ein statisches Kartenbild zur + Wiedererkennung ("das ist die Bretagne"). +- **Alles vertikal stapeln.** Canvas oben, Regler darunter, Ergebniszahl + darunter. Kein Nebeneinander von Canvas und Plot. +- **Touch first:** Regler mit ≥44 px Trefferfläche; der Finger-Modus in + Abschnitt 4 ist bewusst ein Touch-Feature, kein Maus-Feature. `touch-action` + auf dem Zeichen-Canvas setzen, sonst scrollt die Seite beim Malen. +- **Canvas mit `devicePixelRatio` skalieren**, sonst ist das Gitter auf dem + Retina-Display matschig — und ein matschiges Gitter ist hier der Inhalt. +- **Budget: < 100 KB** für alle Küstendaten zusammen (nicht "einige hundert KB + pro Region" wie im ersten Plan). Umrisse als Punktarrays direkt in + `coasts.ts` inlinen, kein Fetch aus `public/`. + +## Datenaufbereitung + +1. GSHHG holen (public domain), Level 1 Küste +2. Auf Bounding Box zuschneiden — Bretagne grob 47,2–48,9 °N / −5,2–−1,0 °E +3. Kontrastregion Normandie zuschneiden (siehe unten) +4. Vereinfachen (Douglas-Peucker) — **so wenig wie möglich**, denn genau die + kleinen Zacken sind das Messobjekt. Die Vereinfachung muss feiner sein als + das kleinste Kästchen, sonst misst das Widget die Vereinfachung statt die + Küste. +5. Auf feste Canvas-Koordinaten projizieren und als Punktarray exportieren + +### Kontrastregion: Normandie + +Gewählt: **Ouistreham bis Le Tréport**, grob 49,2–50,1 °N / −0,3–1,4 °E. + +Begründung — der springende Punkt ist nicht "glatter", sondern **vergleichbarer +Maßstab**: Box-Dimensionen sind nur fair vergleichbar, wenn beide Küsten über +denselben Bereich von Kästchengrößen gemessen werden. Die Normandie-Strecke +ist mit ~200 km ähnlich lang wie die Bretagne-Küste, liegt am selben Meer, hat +dieselben Gezeiten und dieselbe Kartenprojektion. Die einzige Variable, die +sich ändert, ist die Form. Beide liegen zudem auf derselben Frankreichkarte — +für die Kinder unmittelbar einordenbar. + +**Cotentin muss draußen bleiben.** Die Halbinsel (Cap de la Hague) ist selbst +zerklüftet und würde den Kontrast auffressen. Deshalb der Zuschnitt östlich der +Baie des Veys. + +*Verworfene Alternative Usedom:* Außenküste nur ~40 km — bei gleichen +Kästchengrößen würde man einen anderen Größenordnungsbereich messen als bei der +Bretagne. Zweite Falle: die Achterwasser-Seite ist stark verästelt, der +Gesamtumriss der Insel liefert einen *höheren* Wert als erwartet und hätte im +Widget das Gegenteil der Aussage gezeigt. + +**Validierungsschritt vor dem Schreiben (fehlte im ersten Plan):** erst messen, +was die Pipeline tatsächlich ausspuckt, dann den Text schreiben. Über nur ~1,5 +Größenordnungen und mit vereinfachten Daten kommt sehr wahrscheinlich *nicht* +exakt 1,25 heraus. Der Text darf deshalb keine Zahl versprechen, die das Widget +nicht liefert. Formulierung im Post: "ungefähr", plus ein ehrlicher Satz, dass +Mandelbrots Zahl mit besseren Daten gemessen wurde. + +## Tone & Style + +**Kinderton, kein Adressaten-Ton.** Vorbild: "Sendung mit der Maus" / +"Wissen macht Ah!" — die Du-Form bleibt, aber sie meint "du, die Leserin, der +Leser da draußen", nicht bestimmte Kinder mit Namen. Kein "ihr zwei", keine +Familienreferenz. + +- **Deutsch, Du-Form, aber generisch** — "du" als Ansprache an jeden Leser, + keine Namen, keine private Anekdote. +- Kurze Sätze. Kurze Absätze. Fragen statt Behauptungen. +- Erst raten lassen, dann auflösen — das ist das didaktische Rückgrat und der + Grund, warum die Widgets einen "Erst schätzen"-Schritt bekommen. +- Erklärstimme wie bei einem guten Erklärvideo: neugierig, nie belehrend, + gerne kleine Ausrufe ("Überraschung: ...", "Und jetzt kommt der Clou."). +- Keine Notation außerhalb des ``. "Kästchengröße", nicht "ε". + "Wie viel mehr", nicht "Skalierungsverhalten". +- Fachwörter genau zwei: **Dimension** und **Fraktal**. Beide beim ersten + Auftreten in einem Satz erklärt. + +## Sources & Research + +- Mandelbrot, *How Long Is the Coast of Britain? Statistical Self-Similarity + and Fractional Dimension*, Science 156 (1967) — Westküste GB D ≈ 1,25, + Südafrika D ≈ 1,02 als glatter Gegenpol +- Richardson (posthum, 1961) — die Originalmessungen dahinter +- GSHHG, Wessel & Smith — Küstendaten, public domain +- *[TODO: source]* Ein publizierter D-Wert speziell für die Bretagne. Falls + keiner auffindbar: keine Zahl behaupten, nur den selbst gemessenen Wert des + Widgets zeigen und als "unsere Messung" kennzeichnen. + +## Consistency Notes + +**Sprache: geklärt, Deutsch ist etabliert.** `sprit_national.mdx` (2026-05-28, +`tokenID: 197`) ist vollständig auf Deutsch, inklusive deutscher +SEO-Description. Kein Bruch, kein `lang`-Feld nötig — dem Muster dort folgen: +deutscher Titel, deutsche Description, normale `tokenID`. + +**Verwandte Posts (Struktur übernommen):** + +- `housing_risk_portfolio.mdx` und `etf_diversification_interactive.mdx` — + das `.mdx`-Format mit importierten Widget-Komponenten aus + `components/blog/`. Dieser Post folgt diesem Muster, **nicht** dem + `.tsx`-Format von `tragedy_of_commons_fishing.tsx` / + `prisoners_dilemma_interactive.tsx` (Widget und Text in einer Datei, + `export const meta` statt YAML-Frontmatter). Der `.mdx`-Weg hält den + Post-Text als reines Markdown lesbar und die Widgets unabhängig testbar — + passt außerdem besser zum Zielumfang von 900–1200 Wörtern, da die + `.tsx`-Beispiele mit 1600–2000 Zeilen zeigen, wie leicht das Format zum + Anwachsen einlädt. +- `cosmopol_democracy.mdx` (Prolog) und `housing_risk_portfolio.mdx` + (Abendessen-Szene) — beide öffnen mit einer konkreten Szene statt mit dem + Konzept. Abschnitt 1 macht dasselbe mit der Bretagne-Wanderung. +- Die Schreibprinzipien für das Nicht-STEM-Publikum (natürliche Sprache statt + Notation, konkretes Beispiel vor Verallgemeinerung) gelten hier verschärft. + +**Panda-Regeln** aus `website/CLAUDE.md` beachten — insbesondere Regel 1 +(keine JS-Variablen in `css({})`), relevant weil Canvas-Farben in JS *und* CSS +gebraucht werden → über `token()` aus `palette.ts`. + +## Getroffene Entscheidungen + +1. **Sprache:** Deutsch, öffentlicher Blogpost mit `tokenID`, analog + `sprit_national.mdx`. +2. **Kontrastregion: Normandie** (Ouistreham bis Le Tréport). Siehe + "Kontrastregion" oben — Begründung ist der vergleichbare Maßstab, nicht nur + die Glattheit. +3. **Aufmacher: eigenes Foto** der Bretagne-Küste über ``. Kein + Kartenbild nötig — das Widget zeigt den Umriss selbst herausgezoomt. +4. **Finger-Modus: ja.** Die Kinder sind Touch gewohnt; das wird der stärkste + Abschnitt des Posts. + +## Offene Punkte + +- Der gemessene Zahlenwert für beide Küsten steht erst nach Schritt 7 fest. + Bis dahin keine Zahl im Text festschreiben. +- `[TODO: source]` für einen publizierten D-Wert der Bretagne (siehe Sources). + +## Implementierungsschritte + +- [ ] **1** `boxCounting.ts` — reine Zählfunktion + Unit-Tests +- [ ] **2** `shapes.ts` — Linie, Kreis, Quadrat als Punktlisten +- [ ] **3** `BoxCanvas.tsx` — Canvas mit devicePixelRatio, Gitter, Trefferzellen +- [ ] **4** `DimensionSkala.tsx` — Faktor 2…4 → Dimension 1…2 +- [ ] **5** `KaestchenSpiel.tsx` — Widget 1 inkl. "Erst schätzen"-Schritt +- [ ] **6** GSHHG holen, Bretagne + Normandie zuschneiden, vereinfachen, nach + `coasts.ts` inlinen +- [ ] **7** **Messen und validieren** — welche Zahlen liefert das Widget + wirklich für Bretagne und Normandie? Ist der Unterschied auf einem + iPhone-Screen auf einen Blick sichtbar? Wenn nein: Zuschnitt nachjustieren, + nicht den Text schönen. +- [ ] **8** `KuestenSpiel.tsx` — Widget 2, Umschalter Bretagne/Normandie +- [ ] **9** Finger-Modus +- [ ] **10** ``-Exkurs mit Log-Log-Plot via `react-chartjs-2` +- [ ] **11** Post-Text schreiben, auf dem iPhone gegenlesen +- [ ] **12** Quellenangaben (Mandelbrot, GSHHG), `styleConventions.test.ts` grün +- [ ] **13** Nach Veröffentlichung: diese Plandatei löschen + +## Lizenz/Attribution + +- GSHHG: public domain (WVS/WDBII), Quellenangabe trotzdem fair +- OSM/CARTO-Attribution entfällt vollständig, weil keine Tiles und kein + Kartenbild mehr geladen werden — Nebeneffekt der Leaflet-Entscheidung +- Aufmacherfoto: eigenes Bild, keine Lizenzfrage From 8b304bbec11d3213b8678ffcb590fed7dbd26368 Mon Sep 17 00:00:00 2001 From: fretchen Date: Sun, 23 Aug 2026 14:43:49 +0200 Subject: [PATCH 02/12] First draft --- website/blog/kuesten_dimension.mdx | 88 +++++++++++++++++++ .../blog/box-dimension/WidgetPlaceholder.tsx | 35 ++++++++ 2 files changed, 123 insertions(+) create mode 100644 website/blog/kuesten_dimension.mdx create mode 100644 website/components/blog/box-dimension/WidgetPlaceholder.tsx diff --git a/website/blog/kuesten_dimension.mdx b/website/blog/kuesten_dimension.mdx new file mode 100644 index 000000000..0c4728c5d --- /dev/null +++ b/website/blog/kuesten_dimension.mdx @@ -0,0 +1,88 @@ +--- +title: "Wie lang ist die Küste der Bretagne wirklich?" +publishing_date: "2026-09-01" +category: "others" +description: "Eine Küste wird länger, je genauer man sie misst. Mit einem einfachen Kästchen-Trick kannst du herausfinden, wie zerklüftet eine Küste wirklich ist — und dabei eine Zahl entdecken, die niemand erwartet." +--- + +import Foldable from "../components/Foldable"; +import WidgetPlaceholder from "../components/blog/box-dimension/WidgetPlaceholder"; + +Stell dir vor, du schaust dir eine Bucht in der Bretagne auf einer Karte an. Sie sieht klein aus, vielleicht so groß wie dein Daumennagel. Jetzt stell dir vor, du müsstest sie zu Fuß umrunden, immer dicht am Wasser entlang. Um jeden Felsen herum, in jede kleine Nebenbucht hinein, um jeden Stein, der ins Wasser ragt. + +Das würde ewig dauern. Viel länger, als die Karte vermuten lässt. + +Und jetzt kommt die eigentliche Frage: Wie lang ist diese Küste denn nun wirklich? Die Antwort ist überraschend: **Das kommt darauf an, wie genau du hinschaust.** Und genau dieses "Wie genau du hinschaust" lässt sich messen — mit einem Trick, der so einfach ist, dass du ihn sofort selbst ausprobieren kannst. + +## Das Kästchenspiel + +Leg ein Gitter aus lauter gleich großen Kästchen über eine Form. Zähl, wie viele Kästchen die Form berühren. Dann machst du die Kästchen halb so groß und zählst noch einmal. + +Bevor du weiterliest: Was schätzt du — bei einer **geraden Linie**, wie viel mehr Kästchen braucht man, wenn man sie halbiert? + +Probier es im Widget unten aus, an drei Formen: einer geraden Linie, einem Kreis und einem voll ausgemalten Quadrat. + + + +Das Ergebnis: Bei der Linie und beim Kreis brauchst du **doppelt** so viele Kästchen, egal wie oft du halbierst. Beim Quadrat brauchst du **viermal** so viele. + +Das ist kein Zufall. Eine Linie ist eindimensional, egal ob sie gerade oder rund gebogen ist. Eine Fläche ist zweidimensional. Und der Faktor, um den die Kästchenzahl beim Halbieren wächst, hängt genau an dieser Dimension: 2× für Dimension 1, 4× für Dimension 2. + +Wenn du beide Zahlen nebeneinanderlegst, siehst du: Verdoppelt sich der Faktor mit jeder weiteren Dimension. 1 → 2×, 2 → 4×, und eine dritte Dimension (ein Würfel) würde 8× brauchen. + +## Und jetzt eine echte Küste + +Jetzt kommt der Clou. Was passiert, wenn du dasselbe Spiel nicht mit einer Linie, sondern mit einer echten Küste spielst? + + + +Schau dir den Faktor an, den das Widget für die Bretagne anzeigt. Er ist **nicht 2** und **nicht 4**. Er liegt irgendwo dazwischen. + +Das bedeutet: Die Küste ist mehr als eine Linie, aber weniger als eine volle Fläche. Ihre Dimension liegt zwischen 1 und 2 — zum Beispiel bei 1,3. Eine Zahl, die keine ganze Zahl ist, obwohl "Dimension" für dich bisher wahrscheinlich immer 1, 2 oder 3 war. + +Formen, deren Dimension zwischen den ganzen Zahlen liegt, nennt man **Fraktale**. Eine Küste ist ein Fraktal, weil sie in jedem Maßstab neue kleine Zacken zeigt — zoomst du rein, taucht die nächste Miniaturbucht auf, und die nächste, und die nächste. + +Schalte jetzt im Widget auf die Küste der Normandie um. Der Faktor sinkt deutlich. Diese Küste sieht auf der Karte ganz anders aus als die Bretagne: viel glatter, viel gerader, mit weniger kleinen Einbuchtungen. Und genau das zeigt sich auch im gemessenen Faktor — eine glattere Küste liegt näher an der Dimension 1, eine zerklüftete Küste näher an der Dimension 2. + +Der Mathematiker Benoît Mandelbrot hat genau das 1967 zum ersten Mal systematisch gemessen, an der Westküste Großbritanniens. Er kam auf eine Dimension von etwa 1,25 — eine Zahl, die bis heute in jedem Buch über Fraktale steht. + +## Selber malen + +Der beste Weg, ein Gefühl dafür zu bekommen, ist: selbst ausprobieren. + + + +Mal mit dem Finger eine ruhige, geschwungene Linie. Schau dir ihre Dimension an — sie sollte nah bei 1 liegen. Mal jetzt eine wilde, zackige Kritzellinie, so wild du kannst. Die Dimension steigt. + +Du hast damit selbst eine Küste gebaut, nur eben mit dem Finger statt mit dem Meer. Und du hast gemessen, wie "wild" sie ist — mit genau derselben Zahl, die auch für die Bretagne, für Großbritannien und für jede andere Küste der Erde funktioniert. + + + +Der Faktor, den die Widgets oben anzeigen, ist eine vereinfachte Ansicht der klassischen Box-Counting-Dimension: + +$$ +d = \frac{\log N(\epsilon)}{\log(1/\epsilon)} +$$ + +Dabei ist $\epsilon$ die Kantenlänge eines Kästchens und $N(\epsilon)$ die Anzahl der Kästchen, die die Form schneiden. Trägt man $\log N(\epsilon)$ gegen $\log(1/\epsilon)$ für mehrere Kästchengrößen auf, ergibt sich näherungsweise eine Gerade, deren Steigung $d$ ist. + +[TODO: Log-Log-Plot via react-chartjs-2 einfügen, sobald boxCounting.ts über mehrere ε-Werte für Bretagne und Normandie gemessen hat — siehe Schritt 7 im Implementierungsplan.] + +Mandelbrots Originalarbeit: B. Mandelbrot, *How Long Is the Coast of Britain? Statistical Self-Similarity and Fractional Dimension*, Science 156 (1967). Er maß D ≈ 1,25 für die Westküste Großbritanniens und D ≈ 1,02 für die vergleichsweise glatte Küste Südafrikas. + +Die hier verwendeten Küstenumrisse stammen aus der GSHHG-Datenbank (Wessel & Smith), public domain. + + + +--- + +*[TODO: Fred — konkrete Werte für Bretagne und Normandie einsetzen, sobald das Widget echte Daten misst (Implementierungsschritt 7). Bis dahin bleibt der Text bewusst ohne feste Zahl für die Bretagne selbst, außer dem Mandelbrot-Referenzwert für Großbritannien.]* diff --git a/website/components/blog/box-dimension/WidgetPlaceholder.tsx b/website/components/blog/box-dimension/WidgetPlaceholder.tsx new file mode 100644 index 000000000..c002527d6 --- /dev/null +++ b/website/components/blog/box-dimension/WidgetPlaceholder.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import { css } from "../../../styled-system/css"; + +const wrapper = css({ + my: "6", + p: "4", + border: "2px dashed token(colors.border, #e5e7eb)", + borderRadius: "md", + bg: "gray.50", + textAlign: "center", + color: "gray.600", + fontSize: "sm", +}); + +export interface WidgetPlaceholderProps { + /** Working title of the widget, shown as the placeholder heading. */ + label: string; + /** One-line description of what it will do, for the reviewer, not the reader. */ + description: string; +} + +/** + * Stand-in for an interactive widget that has not been implemented yet. + * Lets the surrounding post text be reviewed before the box-counting engine exists. + */ +export function WidgetPlaceholder({ label, description }: WidgetPlaceholderProps) { + return ( +
+ 🚧 {label} +

{description}

+
+ ); +} + +export default WidgetPlaceholder; From 615ab50363ad0ca40f6a9afa2333d855de10709f Mon Sep 17 00:00:00 2001 From: fretchen Date: Sun, 23 Aug 2026 18:34:02 +0200 Subject: [PATCH 03/12] First round --- notebooks/.gitignore | 4 + notebooks/box_dimension_coastlines.py | 332 + notebooks/output/box_dimension_coasts.json | 7426 +++++++++++++++++ notebooks/pyproject.toml | 2 + notebooks/uv.lock | 151 +- website/blog/kuesten_dimension.mdx | 30 +- .../blog/box-dimension/BoxCanvas.tsx | 173 + .../blog/box-dimension/DimensionSkala.tsx | 91 + .../blog/box-dimension/KaestchenSpiel.tsx | 178 + .../blog/box-dimension/KuestenSpiel.tsx | 139 + .../blog/box-dimension/Messreihe.tsx | 85 + .../blog/box-dimension/WidgetPlaceholder.tsx | 35 - .../blog/box-dimension/boxCounting.ts | 170 + .../components/blog/box-dimension/coasts.ts | 1907 +++++ .../components/blog/box-dimension/shapes.ts | 55 + website/package.json | 3 +- website/test/boxCounting.test.ts | 78 + website/test/dimensionSkala.test.ts | 75 + website/utils/coastsToTs.ts | 92 + 19 files changed, 10934 insertions(+), 92 deletions(-) create mode 100644 notebooks/box_dimension_coastlines.py create mode 100644 notebooks/output/box_dimension_coasts.json create mode 100644 website/components/blog/box-dimension/BoxCanvas.tsx create mode 100644 website/components/blog/box-dimension/DimensionSkala.tsx create mode 100644 website/components/blog/box-dimension/KaestchenSpiel.tsx create mode 100644 website/components/blog/box-dimension/KuestenSpiel.tsx create mode 100644 website/components/blog/box-dimension/Messreihe.tsx delete mode 100644 website/components/blog/box-dimension/WidgetPlaceholder.tsx create mode 100644 website/components/blog/box-dimension/boxCounting.ts create mode 100644 website/components/blog/box-dimension/coasts.ts create mode 100644 website/components/blog/box-dimension/shapes.ts create mode 100644 website/test/boxCounting.test.ts create mode 100644 website/test/dimensionSkala.test.ts create mode 100644 website/utils/coastsToTs.ts diff --git a/notebooks/.gitignore b/notebooks/.gitignore index 4c49bd78f..10ddf953e 100644 --- a/notebooks/.gitignore +++ b/notebooks/.gitignore @@ -1 +1,5 @@ .env + +# raw GSHHG download for box_dimension_coastlines.py — multi-hundred MB, re-downloaded on demand +.data/ + diff --git a/notebooks/box_dimension_coastlines.py b/notebooks/box_dimension_coastlines.py new file mode 100644 index 000000000..eceadde58 --- /dev/null +++ b/notebooks/box_dimension_coastlines.py @@ -0,0 +1,332 @@ +#!/usr/bin/env python3 +""" +Prepares the coastline data for the box-dimension blog widgets. + +Downloads GSHHG (public domain), clips it to two bounding boxes (Bretagne and +a Normandy stretch), lightly simplifies each outline, projects it into the +small local coordinate system the browser widgets use, and writes the result +as JSON. + +Run: + uv run python box_dimension_coastlines.py + +Re-running is safe and idempotent — the GSHHG download is cached under +`.data/gshhg/` (gitignored) and skipped if already present. The two bounding +boxes and the simplification tolerance are the reproducible inputs: change a +constant below and re-run to regenerate the output. + +The JSON this writes (`output/box_dimension_coasts.json`) is *not* consumed +directly by the website — `website/utils/coastsToTs.ts` turns it into the +committed `coasts.ts` module. Keeping the split this way means all the actual +geo-processing stays in Python (shapely has far more mature tooling for this +than anything in the Node ecosystem), while the artifact that actually ships +to the browser is a small, versioned, machine-generated TypeScript file. +""" + +import io +import json +import math +import zipfile +from dataclasses import dataclass +from pathlib import Path + +import requests +import shapefile # pyshp +from shapely.geometry import LineString, Polygon, box +from shapely.ops import unary_union + +GSHHG_VERSION = "2.3.7" +GSHHG_URL = f"https://www.soest.hawaii.edu/pwessel/gshhg/gshhg-shp-{GSHHG_VERSION}.zip" + +DATA_DIR = Path(__file__).parent / ".data" / "gshhg" +OUTPUT_PATH = Path(__file__).parent / "output" / "box_dimension_coasts.json" + +# GSHHS Level 1 = coastline (mainland + islands), highest ("f"ull) resolution — +# needed so the small-scale jaggedness the widgets measure survives at all. +LEVEL1_SHAPEFILE = "GSHHS_shp/f/GSHHS_f_L1.shp" + +WORLD_SIZE = 100 # must match website/components/blog/box-dimension/shapes.ts WORLD_SIZE +TARGET_POINT_BUDGET = 2500 # per region — keeps combined coasts.ts comfortably under 100KB + + +@dataclass +class BBox: + lon_min: float + lon_max: float + lat_min: float + lat_max: float + + def to_shapely(self) -> "box": + return box(self.lon_min, self.lat_min, self.lon_max, self.lat_max) + + +# Bounding boxes as pinned by the approved blog post plan (kuesten_dimension.plan.md). +REGIONS: dict[str, BBox] = { + "bretagne": BBox(lon_min=-5.2, lon_max=-1.0, lat_min=47.2, lat_max=48.9), + # Ouistreham to Le Tréport, deliberately excluding the Cotentin peninsula + # (Cap de la Hague) — that stretch is itself jagged and would muddy the + # "smooth contrast coast" comparison the post makes against Bretagne. + "normandie": BBox(lon_min=-0.3, lon_max=1.4, lat_min=49.2, lat_max=50.1), +} + +# Douglas-Peucker tolerance in degrees. Must stay well below the real-world +# size of the widget's smallest grid cell for either region, or the widget +# would measure the simplification artifact instead of the actual coastline. +# 0.0025 deg is ~200-280m at these latitudes, vs. a ~4km smallest cell (see +# the printed "Shared scale" line below) — a ~14-20x margin. Chosen mainly to +# hit the <100KB combined output budget; see step 7 (Validierung) of the blog +# plan for the check that this doesn't erase the jaggedness being measured. +SIMPLIFY_TOLERANCE_DEG = 0.0025 + + +def download_gshhg() -> Path: + shapefile_path = DATA_DIR / LEVEL1_SHAPEFILE + if shapefile_path.exists(): + print(f"Using cached GSHHG data at {DATA_DIR}") + return shapefile_path + + print(f"Downloading GSHHG {GSHHG_VERSION} from {GSHHG_URL} (~150MB)...") + DATA_DIR.mkdir(parents=True, exist_ok=True) + response = requests.get(GSHHG_URL, timeout=300) + response.raise_for_status() + + print("Extracting...") + with zipfile.ZipFile(io.BytesIO(response.content)) as zf: + zf.extractall(DATA_DIR) + + if not shapefile_path.exists(): + raise FileNotFoundError( + f"Expected {shapefile_path} after extraction — GSHHG layout may have changed." + ) + + return shapefile_path + + +def load_level1_polygons(shapefile_path: Path, bbox: BBox) -> list[Polygon]: + """Reads GSHHS Level 1 records whose bounding box overlaps the target bbox.""" + sf = shapefile.Reader(str(shapefile_path)) + target = bbox.to_shapely() + polygons: list[Polygon] = [] + + for shape_rec in sf.iterShapeRecords(): + shp = shape_rec.shape + rec_lon_min, rec_lat_min, rec_lon_max, rec_lat_max = shp.bbox + if rec_lon_max < bbox.lon_min or rec_lon_min > bbox.lon_max: + continue + if rec_lat_max < bbox.lat_min or rec_lat_min > bbox.lat_max: + continue + + points = shp.points + parts = list(shp.parts) + [len(points)] + for i in range(len(parts) - 1): + ring = points[parts[i] : parts[i + 1]] + if len(ring) < 4: + continue + poly = Polygon(ring) + if not poly.is_valid: + poly = poly.buffer(0) + if poly.intersects(target): + polygons.append(poly) + + return polygons + + +def extract_coastline(polygons: list[Polygon], bbox: BBox) -> LineString: + target = bbox.to_shapely() + merged = unary_union(polygons) + clipped = merged.intersection(target) + + boundary = clipped.boundary + candidates = list(boundary.geoms) if hasattr(boundary, "geoms") else [boundary] + + real_coastline_parts: list[LineString] = [] + for geom in candidates: + if not isinstance(geom, LineString): + continue + coords = list(geom.coords) + # Drop sub-segments that run entirely along the bbox frame (clip artifacts). + # A ring can still mix real coastline with a frame-hugging closing edge, + # so also drop maximal runs of frame-only points within a ring. + filtered = _drop_frame_runs(coords, bbox) + real_coastline_parts.extend(filtered) + + if not real_coastline_parts: + raise ValueError(f"No real coastline found for bbox {bbox} — check the bbox against a map.") + + # Keep the longest remaining stretch (excludes offshore islands/fragments). + longest = max(real_coastline_parts, key=lambda ls: ls.length) + return longest + + +def _drop_frame_runs( + coords: list[tuple[float, float]], bbox: BBox, eps: float = 1e-7 +) -> list[LineString]: + """Splits a ring's coordinates into runs, dropping runs glued to the bbox frame.""" + + def on_frame(pt: tuple[float, float]) -> bool: + x, y = pt + return ( + abs(x - bbox.lon_min) < eps + or abs(x - bbox.lon_max) < eps + or abs(y - bbox.lat_min) < eps + or abs(y - bbox.lat_max) < eps + ) + + runs: list[LineString] = [] + current: list[tuple[float, float]] = [] + for pt in coords: + if on_frame(pt): + if len(current) >= 2: + runs.append(LineString(current)) + current = [] + else: + current.append(pt) + if len(current) >= 2: + runs.append(LineString(current)) + return runs + + +KM_PER_DEG = 111.32 # rough, fine at these latitudes + + +def project_to_km(line: LineString) -> tuple[list[tuple[float, float]], dict]: + """Equirectangular projection into real kilometres (not yet fit to WORLD_SIZE). + + Longitude is scaled by cos(mean latitude) so x/y are both in true km — a + single shared scale (computed later, across all regions) then converts km + into world units. Deferring that scale is what keeps the comparison fair: + if each region picked its own scale to fill the world box edge-to-edge, the + same slider cellSize would silently mean a different real-world distance + for each region. + """ + coords = list(line.coords) + lats = [c[1] for c in coords] + lons = [c[0] for c in coords] + lat_mean = sum(lats) / len(lats) + cos_lat = math.cos(math.radians(lat_mean)) + + lon0, lat0 = lons[0], lats[0] + km_coords = [ + ((lon - lon0) * cos_lat * KM_PER_DEG, -(lat - lat0) * KM_PER_DEG) + for lon, lat in zip(lons, lats) + ] + + bbox_meta = { + "lonMin": min(lons), + "lonMax": max(lons), + "latMin": min(lats), + "latMax": max(lats), + } + return km_coords, bbox_meta + + +def normalize_with_shared_scale( + km_coords: list[tuple[float, float]], scale: float +) -> list[list[float]]: + xs = [p[0] for p in km_coords] + ys = [p[1] for p in km_coords] + x_min, y_min = min(xs), min(ys) + return [[round((x - x_min) * scale, 3), round((y - y_min) * scale, 3)] for x, y in km_coords] + + +def _haversine_length_km(coords: list[tuple[float, float]]) -> float: + r_earth = 6371.0 + total = 0.0 + for (lon1, lat1), (lon2, lat2) in zip(coords, coords[1:]): + phi1, phi2 = math.radians(lat1), math.radians(lat2) + dphi = math.radians(lat2 - lat1) + dlambda = math.radians(lon2 - lon1) + a = math.sin(dphi / 2) ** 2 + math.cos(phi1) * math.cos(phi2) * math.sin(dlambda / 2) ** 2 + total += 2 * r_earth * math.asin(math.sqrt(a)) + return total + + +def process_region(name: str, bbox: BBox, shapefile_path: Path) -> dict: + print(f"\n--- {name} ---") + polygons = load_level1_polygons(shapefile_path, bbox) + print(f" {len(polygons)} candidate polygon(s) overlap the bounding box") + + coastline = extract_coastline(polygons, bbox) + raw_km = _haversine_length_km(list(coastline.coords)) + print(f" raw coastline: {len(coastline.coords)} points, {raw_km:.1f} km") + + simplified = coastline.simplify(SIMPLIFY_TOLERANCE_DEG, preserve_topology=True) + simplified_km = _haversine_length_km(list(simplified.coords)) + print( + f" simplified: {len(simplified.coords)} points, {simplified_km:.1f} km " + f"(tolerance={SIMPLIFY_TOLERANCE_DEG} deg)" + ) + + if len(simplified.coords) > TARGET_POINT_BUDGET: + print( + f" WARNING: {len(simplified.coords)} points exceeds the " + f"{TARGET_POINT_BUDGET} budget — consider a coarser SIMPLIFY_TOLERANCE_DEG." + ) + + km_coords, bbox_meta = project_to_km(simplified) + x_range = max(x for x, _ in km_coords) - min(x for x, _ in km_coords) + y_range = max(y for _, y in km_coords) - min(y for _, y in km_coords) + + return { + "name": name, + "km_coords": km_coords, + "sourceLengthKm": simplified_km, + "boundingBox": bbox_meta, + "spanKm": max(x_range, y_range), + } + + +def main(): + shapefile_path = download_gshhg() + + regions_raw = { + name: process_region(name, bbox, shapefile_path) for name, bbox in REGIONS.items() + } + + # One shared scale (world-units per km) for all regions — the region with the + # largest physical span fills the WORLD_SIZE box; every other region uses the + # exact same km-to-world-unit ratio, so a given slider cellSize always means + # the same real-world distance no matter which region is showing. + reference_span_km = max(r["spanKm"] for r in regions_raw.values()) + shared_scale = WORLD_SIZE / reference_span_km + min_cell_size_world = 1.5625 # smallest step in KaestchenSpiel/KuestenSpiel's CELL_SIZE_STEPS + min_cell_size_real_km = min_cell_size_world / shared_scale + + print( + f"\nShared scale: {shared_scale:.4f} world-units/km " + f"(reference span {reference_span_km:.1f} km)" + ) + print( + f"Smallest slider cell size ({min_cell_size_world} world-units) = " + f"{min_cell_size_real_km:.2f} km real" + ) + + regions_out = {} + for name, raw in regions_raw.items(): + points = normalize_with_shared_scale(raw["km_coords"], shared_scale) + meta = { + "sourceLengthKm": raw["sourceLengthKm"], + "simplificationToleranceDeg": SIMPLIFY_TOLERANCE_DEG, + "minCellSizeWorld": min_cell_size_world, + "minCellSizeRealKm": min_cell_size_real_km, + "boundingBox": raw["boundingBox"], + } + approx_bytes = len(json.dumps(points)) + print( + f" {name}: {len(points)} points, ~{approx_bytes / 1024:.1f} KB, span used " + f"{raw['spanKm'] * shared_scale:.1f}/{WORLD_SIZE} world-units" + ) + regions_out[name] = {"name": name, "points": points, "worldSize": WORLD_SIZE, "meta": meta} + + OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) + with open(OUTPUT_PATH, "w") as f: + json.dump(regions_out, f, indent=2) + + total_bytes = OUTPUT_PATH.stat().st_size + print(f"\nWrote {OUTPUT_PATH} ({total_bytes / 1024:.1f} KB total)") + if total_bytes > 100_000: + print("WARNING: exceeds the ~100KB combined budget from the blog plan.") + + +if __name__ == "__main__": + main() diff --git a/notebooks/output/box_dimension_coasts.json b/notebooks/output/box_dimension_coasts.json new file mode 100644 index 000000000..a66f95f0f --- /dev/null +++ b/notebooks/output/box_dimension_coasts.json @@ -0,0 +1,7426 @@ +{ + "bretagne": { + "name": "bretagne", + "points": [ + [ + 76.634, + 73.821 + ], + [ + 76.561, + 73.278 + ], + [ + 75.982, + 72.55 + ], + [ + 75.993, + 72.352 + ], + [ + 76.162, + 72.35 + ], + [ + 76.296, + 72.081 + ], + [ + 76.331, + 71.009 + ], + [ + 77.372, + 70.682 + ], + [ + 78.222, + 70.684 + ], + [ + 78.536, + 70.502 + ], + [ + 78.631, + 70.647 + ], + [ + 78.802, + 70.464 + ], + [ + 79.434, + 70.355 + ], + [ + 79.081, + 70.229 + ], + [ + 79.458, + 69.957 + ], + [ + 80.475, + 69.995 + ], + [ + 81.032, + 70.393 + ], + [ + 81.393, + 70.467 + ], + [ + 80.994, + 70.228 + ], + [ + 81.055, + 70.101 + ], + [ + 82.316, + 70.429 + ], + [ + 82.525, + 70.272 + ], + [ + 82.582, + 69.937 + ], + [ + 82.348, + 69.525 + ], + [ + 80.789, + 68.904 + ], + [ + 80.11, + 68.902 + ], + [ + 79.166, + 69.377 + ], + [ + 77.544, + 69.449 + ], + [ + 76.332, + 70.029 + ], + [ + 76.019, + 69.667 + ], + [ + 75.885, + 70.083 + ], + [ + 75.982, + 70.191 + ], + [ + 75.933, + 70.337 + ], + [ + 75.676, + 70.392 + ], + [ + 75.484, + 71.045 + ], + [ + 74.999, + 71.01 + ], + [ + 74.745, + 71.172 + ], + [ + 74.733, + 71.482 + ], + [ + 73.91, + 71.663 + ], + [ + 73.57, + 72.28 + ], + [ + 72.675, + 72.533 + ], + [ + 71.39, + 71.553 + ], + [ + 70.979, + 70.9 + ], + [ + 70.008, + 70.464 + ], + [ + 69.378, + 70.502 + ], + [ + 68.945, + 70.719 + ], + [ + 69.196, + 71.316 + ], + [ + 69.137, + 71.48 + ], + [ + 68.215, + 71.336 + ], + [ + 67.782, + 70.937 + ], + [ + 67.466, + 71.01 + ], + [ + 66.134, + 70.21 + ], + [ + 65.396, + 69.974 + ], + [ + 65.456, + 69.666 + ], + [ + 66.133, + 69.521 + ], + [ + 66.401, + 69.667 + ], + [ + 66.714, + 70.246 + ], + [ + 66.982, + 70.32 + ], + [ + 67.079, + 70.066 + ], + [ + 67.393, + 70.211 + ], + [ + 67.259, + 69.939 + ], + [ + 67.601, + 69.829 + ], + [ + 67.393, + 69.631 + ], + [ + 66.775, + 69.54 + ], + [ + 66.86, + 69.375 + ], + [ + 66.981, + 69.484 + ], + [ + 67.466, + 69.23 + ], + [ + 67.321, + 68.941 + ], + [ + 67.176, + 69.085 + ], + [ + 66.668, + 69.086 + ], + [ + 66.328, + 69.522 + ], + [ + 66.655, + 68.743 + ], + [ + 66.559, + 67.797 + ], + [ + 65.963, + 66.835 + ], + [ + 65.188, + 66.509 + ], + [ + 65.079, + 66.308 + ], + [ + 65.335, + 66.036 + ], + [ + 65.965, + 65.818 + ], + [ + 66.789, + 65.02 + ], + [ + 67.031, + 65.02 + ], + [ + 67.129, + 64.728 + ], + [ + 67.418, + 64.693 + ], + [ + 67.733, + 64.295 + ], + [ + 67.902, + 64.73 + ], + [ + 68.047, + 64.548 + ], + [ + 68.24, + 64.695 + ], + [ + 68.739, + 64.675 + ], + [ + 68.036, + 64.096 + ], + [ + 68.193, + 63.678 + ], + [ + 68.423, + 63.658 + ], + [ + 68.072, + 63.17 + ], + [ + 67.636, + 63.098 + ], + [ + 67.345, + 63.462 + ], + [ + 66.921, + 63.078 + ], + [ + 66.776, + 62.681 + ], + [ + 67.091, + 62.245 + ], + [ + 67.041, + 61.882 + ], + [ + 66.704, + 61.409 + ], + [ + 66.836, + 61.064 + ], + [ + 67.346, + 61.065 + ], + [ + 67.66, + 61.39 + ], + [ + 68.314, + 61.101 + ], + [ + 69.452, + 61.209 + ], + [ + 69.841, + 60.775 + ], + [ + 70.081, + 60.956 + ], + [ + 70.106, + 60.737 + ], + [ + 69.768, + 60.629 + ], + [ + 69.331, + 61.03 + ], + [ + 68.411, + 60.917 + ], + [ + 67.733, + 60.41 + ], + [ + 66.739, + 60.195 + ], + [ + 66.92, + 59.848 + ], + [ + 66.837, + 59.72 + ], + [ + 66.764, + 60.085 + ], + [ + 66.376, + 59.76 + ], + [ + 65.455, + 59.94 + ], + [ + 65.467, + 60.14 + ], + [ + 65.225, + 60.32 + ], + [ + 65.31, + 60.519 + ], + [ + 64.704, + 60.23 + ], + [ + 64.027, + 60.156 + ], + [ + 63.809, + 60.193 + ], + [ + 63.591, + 60.592 + ], + [ + 63.323, + 60.737 + ], + [ + 62.875, + 60.72 + ], + [ + 62.936, + 60.484 + ], + [ + 63.057, + 60.557 + ], + [ + 63.228, + 60.302 + ], + [ + 63.421, + 60.265 + ], + [ + 63.362, + 60.429 + ], + [ + 63.493, + 60.484 + ], + [ + 63.579, + 60.067 + ], + [ + 63.458, + 59.884 + ], + [ + 63.905, + 59.613 + ], + [ + 64.147, + 59.65 + ], + [ + 64.427, + 59.232 + ], + [ + 64.571, + 58.795 + ], + [ + 64.536, + 58.668 + ], + [ + 64.293, + 59.178 + ], + [ + 63.76, + 59.65 + ], + [ + 63.349, + 59.032 + ], + [ + 63.433, + 59.377 + ], + [ + 63.325, + 59.541 + ], + [ + 62.596, + 59.467 + ], + [ + 63.24, + 59.739 + ], + [ + 63.105, + 60.194 + ], + [ + 62.017, + 60.156 + ], + [ + 62.113, + 59.975 + ], + [ + 61.482, + 59.721 + ], + [ + 61.931, + 60.138 + ], + [ + 61.882, + 60.611 + ], + [ + 61.434, + 61.138 + ], + [ + 60.708, + 60.773 + ], + [ + 59.906, + 60.666 + ], + [ + 59.786, + 60.885 + ], + [ + 59.302, + 61.065 + ], + [ + 58.503, + 61.174 + ], + [ + 58.188, + 61.574 + ], + [ + 56.712, + 61.065 + ], + [ + 56.577, + 60.938 + ], + [ + 56.698, + 60.575 + ], + [ + 56.65, + 60.321 + ], + [ + 56.346, + 60.084 + ], + [ + 56.227, + 59.758 + ], + [ + 55.401, + 59.212 + ], + [ + 55.089, + 59.395 + ], + [ + 54.954, + 59.306 + ], + [ + 55.355, + 58.922 + ], + [ + 54.773, + 59.069 + ], + [ + 54.712, + 58.904 + ], + [ + 54.857, + 58.651 + ], + [ + 54.664, + 58.434 + ], + [ + 54.82, + 58.414 + ], + [ + 54.894, + 58.126 + ], + [ + 54.99, + 58.305 + ], + [ + 55.184, + 58.161 + ], + [ + 55.257, + 58.451 + ], + [ + 55.427, + 58.234 + ], + [ + 55.62, + 58.488 + ], + [ + 55.511, + 58.142 + ], + [ + 55.79, + 58.053 + ], + [ + 55.814, + 58.488 + ], + [ + 56.069, + 58.396 + ], + [ + 55.924, + 58.759 + ], + [ + 56.322, + 58.633 + ], + [ + 56.358, + 58.942 + ], + [ + 56.237, + 59.086 + ], + [ + 56.517, + 59.248 + ], + [ + 56.723, + 59.16 + ], + [ + 56.505, + 59.087 + ], + [ + 56.662, + 58.816 + ], + [ + 56.976, + 58.961 + ], + [ + 57.294, + 58.705 + ], + [ + 57.207, + 58.941 + ], + [ + 57.28, + 59.23 + ], + [ + 57.341, + 59.288 + ], + [ + 57.329, + 59.013 + ], + [ + 57.499, + 58.977 + ], + [ + 57.474, + 58.723 + ], + [ + 57.327, + 58.65 + ], + [ + 57.365, + 58.414 + ], + [ + 57.872, + 58.453 + ], + [ + 57.861, + 59.124 + ], + [ + 58.091, + 59.215 + ], + [ + 58.151, + 59.013 + ], + [ + 58.056, + 58.868 + ], + [ + 58.334, + 58.633 + ], + [ + 58.527, + 58.85 + ], + [ + 58.733, + 58.83 + ], + [ + 58.623, + 59.141 + ], + [ + 59.037, + 58.996 + ], + [ + 59.131, + 59.25 + ], + [ + 59.302, + 59.285 + ], + [ + 59.763, + 58.996 + ], + [ + 60.005, + 59.031 + ], + [ + 59.921, + 58.65 + ], + [ + 60.162, + 58.287 + ], + [ + 60.115, + 57.962 + ], + [ + 60.43, + 57.707 + ], + [ + 60.38, + 57.163 + ], + [ + 60.611, + 57.073 + ], + [ + 60.659, + 57.364 + ], + [ + 60.878, + 57.362 + ], + [ + 60.72, + 57.307 + ], + [ + 60.852, + 56.964 + ], + [ + 61.023, + 57.144 + ], + [ + 61.095, + 56.962 + ], + [ + 61.252, + 56.981 + ], + [ + 61.106, + 56.763 + ], + [ + 61.253, + 56.689 + ], + [ + 61.203, + 56.218 + ], + [ + 61.349, + 56.074 + ], + [ + 60.804, + 54.859 + ], + [ + 60.817, + 55.24 + ], + [ + 61.01, + 55.419 + ], + [ + 61.131, + 55.82 + ], + [ + 60.951, + 56.275 + ], + [ + 60.683, + 56.02 + ], + [ + 60.622, + 56.037 + ], + [ + 60.889, + 56.327 + ], + [ + 60.854, + 56.528 + ], + [ + 60.598, + 56.727 + ], + [ + 60.661, + 56.926 + ], + [ + 60.418, + 56.927 + ], + [ + 60.318, + 56.454 + ], + [ + 60.127, + 56.528 + ], + [ + 60.053, + 56.347 + ], + [ + 59.933, + 56.345 + ], + [ + 60.066, + 56.51 + ], + [ + 60.007, + 56.6 + ], + [ + 59.255, + 55.911 + ], + [ + 58.636, + 55.675 + ], + [ + 58.698, + 55.438 + ], + [ + 58.841, + 55.439 + ], + [ + 59.969, + 55.928 + ], + [ + 59.726, + 55.71 + ], + [ + 59.752, + 55.457 + ], + [ + 59.886, + 55.368 + ], + [ + 59.667, + 55.293 + ], + [ + 59.606, + 55.675 + ], + [ + 59.459, + 55.638 + ], + [ + 59.291, + 55.276 + ], + [ + 59.387, + 54.948 + ], + [ + 59.24, + 54.803 + ], + [ + 59.254, + 54.532 + ], + [ + 59.143, + 55.166 + ], + [ + 58.866, + 55.258 + ], + [ + 58.429, + 55.111 + ], + [ + 58.346, + 54.947 + ], + [ + 58.54, + 54.694 + ], + [ + 58.309, + 54.82 + ], + [ + 58.116, + 54.459 + ], + [ + 58.223, + 54.949 + ], + [ + 58.637, + 55.349 + ], + [ + 58.553, + 55.62 + ], + [ + 58.093, + 55.802 + ], + [ + 57.533, + 55.62 + ], + [ + 57.097, + 56.02 + ], + [ + 57.012, + 55.964 + ], + [ + 57.193, + 55.693 + ], + [ + 57.003, + 55.658 + ], + [ + 57.039, + 55.783 + ], + [ + 56.929, + 55.837 + ], + [ + 56.445, + 55.765 + ], + [ + 56.467, + 55.62 + ], + [ + 56.325, + 55.584 + ], + [ + 56.069, + 55.747 + ], + [ + 56.383, + 55.857 + ], + [ + 56.117, + 56.218 + ], + [ + 56.311, + 56.436 + ], + [ + 56.227, + 56.71 + ], + [ + 55.476, + 56.382 + ], + [ + 55.245, + 56.728 + ], + [ + 55.463, + 56.69 + ], + [ + 55.355, + 57.328 + ], + [ + 55.112, + 57.109 + ], + [ + 54.844, + 57.29 + ], + [ + 54.955, + 56.982 + ], + [ + 54.844, + 56.889 + ], + [ + 54.555, + 57.327 + ], + [ + 54.373, + 56.946 + ], + [ + 54.567, + 56.909 + ], + [ + 54.58, + 56.456 + ], + [ + 54.361, + 56.819 + ], + [ + 54.275, + 56.364 + ], + [ + 54.374, + 56.185 + ], + [ + 54.226, + 56.254 + ], + [ + 54.083, + 56.691 + ], + [ + 54.154, + 57.127 + ], + [ + 54.071, + 57.145 + ], + [ + 53.695, + 56.147 + ], + [ + 53.817, + 55.966 + ], + [ + 53.743, + 55.566 + ], + [ + 54.106, + 55.385 + ], + [ + 53.998, + 55.258 + ], + [ + 53.475, + 55.347 + ], + [ + 53.38, + 55.093 + ], + [ + 53.475, + 54.912 + ], + [ + 53.185, + 54.84 + ], + [ + 53.259, + 54.476 + ], + [ + 52.944, + 54.116 + ], + [ + 52.93, + 53.842 + ], + [ + 52.822, + 54.151 + ], + [ + 52.92, + 54.476 + ], + [ + 53.114, + 54.658 + ], + [ + 53.136, + 55.022 + ], + [ + 53.38, + 55.166 + ], + [ + 53.598, + 55.93 + ], + [ + 53.271, + 56.019 + ], + [ + 53.355, + 56.183 + ], + [ + 53.185, + 56.256 + ], + [ + 53.575, + 56.581 + ], + [ + 53.307, + 57.017 + ], + [ + 53.415, + 57.292 + ], + [ + 53.78, + 57.436 + ], + [ + 53.817, + 57.744 + ], + [ + 54.144, + 58.232 + ], + [ + 54.349, + 58.29 + ], + [ + 54.263, + 58.523 + ], + [ + 54.094, + 58.561 + ], + [ + 53.633, + 58.451 + ], + [ + 53.464, + 58.088 + ], + [ + 53.294, + 58.561 + ], + [ + 52.956, + 58.378 + ], + [ + 52.906, + 58.161 + ], + [ + 53.113, + 58.251 + ], + [ + 52.968, + 57.961 + ], + [ + 53.04, + 57.815 + ], + [ + 52.833, + 57.507 + ], + [ + 52.485, + 57.38 + ], + [ + 52.738, + 57.038 + ], + [ + 52.448, + 57.181 + ], + [ + 52.289, + 56.982 + ], + [ + 52.568, + 57.942 + ], + [ + 52.35, + 57.761 + ], + [ + 52.301, + 58.015 + ], + [ + 52.01, + 58.016 + ], + [ + 51.708, + 57.128 + ], + [ + 51.803, + 56.618 + ], + [ + 51.612, + 56.509 + ], + [ + 51.612, + 56.076 + ], + [ + 51.443, + 55.784 + ], + [ + 51.611, + 55.53 + ], + [ + 51.332, + 55.548 + ], + [ + 51.236, + 54.896 + ], + [ + 51.162, + 55.185 + ], + [ + 50.897, + 55.185 + ], + [ + 50.885, + 55.24 + ], + [ + 51.189, + 55.219 + ], + [ + 51.248, + 55.603 + ], + [ + 51.393, + 55.709 + ], + [ + 51.514, + 56.255 + ], + [ + 51.393, + 56.509 + ], + [ + 51.514, + 56.872 + ], + [ + 51.418, + 56.944 + ], + [ + 51.647, + 57.945 + ], + [ + 51.067, + 57.688 + ], + [ + 51.199, + 57.417 + ], + [ + 51.089, + 57.255 + ], + [ + 51.067, + 57.509 + ], + [ + 50.704, + 57.544 + ], + [ + 50.677, + 57.832 + ], + [ + 50.316, + 57.726 + ], + [ + 49.418, + 58.125 + ], + [ + 49.091, + 57.633 + ], + [ + 49.178, + 57.471 + ], + [ + 49.541, + 57.579 + ], + [ + 49.528, + 57.344 + ], + [ + 49.226, + 57.399 + ], + [ + 49.275, + 57.183 + ], + [ + 49.078, + 57.183 + ], + [ + 48.523, + 56.601 + ], + [ + 48.461, + 57.634 + ], + [ + 48.873, + 57.561 + ], + [ + 48.486, + 58.216 + ], + [ + 48.39, + 58.978 + ], + [ + 48.486, + 59.921 + ], + [ + 48.608, + 60.356 + ], + [ + 48.801, + 60.465 + ], + [ + 48.802, + 60.973 + ], + [ + 49.031, + 61.319 + ], + [ + 49.541, + 61.646 + ], + [ + 49.6, + 61.989 + ], + [ + 49.891, + 62.208 + ], + [ + 49.782, + 62.26 + ], + [ + 48.838, + 61.899 + ], + [ + 48.427, + 62.044 + ], + [ + 48.075, + 61.626 + ], + [ + 48.075, + 61.263 + ], + [ + 47.833, + 60.755 + ], + [ + 47.904, + 60.539 + ], + [ + 47.807, + 59.994 + ], + [ + 47.542, + 59.704 + ], + [ + 48.015, + 59.576 + ], + [ + 48.293, + 58.868 + ], + [ + 48.195, + 57.309 + ], + [ + 47.639, + 56.291 + ], + [ + 47.857, + 56.074 + ], + [ + 47.505, + 56.128 + ], + [ + 46.681, + 55.547 + ], + [ + 46.416, + 55.548 + ], + [ + 46.452, + 55.277 + ], + [ + 46.161, + 54.802 + ], + [ + 46.38, + 54.15 + ], + [ + 46.257, + 54.041 + ], + [ + 46.246, + 53.732 + ], + [ + 46.839, + 53.859 + ], + [ + 46.706, + 53.623 + ], + [ + 46.512, + 53.733 + ], + [ + 46.184, + 53.57 + ], + [ + 46.536, + 53.406 + ], + [ + 46.525, + 53.206 + ], + [ + 46.658, + 53.079 + ], + [ + 46.803, + 53.15 + ], + [ + 46.925, + 52.642 + ], + [ + 47.043, + 52.861 + ], + [ + 47.602, + 53.044 + ], + [ + 47.542, + 52.808 + ], + [ + 47.651, + 52.643 + ], + [ + 47.82, + 52.79 + ], + [ + 48.109, + 52.282 + ], + [ + 48.449, + 52.209 + ], + [ + 48.872, + 52.37 + ], + [ + 48.643, + 51.955 + ], + [ + 48.233, + 52.172 + ], + [ + 48.037, + 52.063 + ], + [ + 48.063, + 52.244 + ], + [ + 47.773, + 52.245 + ], + [ + 47.807, + 52.406 + ], + [ + 47.675, + 52.462 + ], + [ + 47.347, + 52.299 + ], + [ + 47.942, + 51.991 + ], + [ + 47.88, + 51.72 + ], + [ + 47.966, + 51.408 + ], + [ + 48.123, + 51.429 + ], + [ + 48.087, + 51.775 + ], + [ + 48.572, + 51.917 + ], + [ + 48.487, + 51.503 + ], + [ + 48.849, + 51.32 + ], + [ + 48.595, + 51.19 + ], + [ + 48.595, + 51.046 + ], + [ + 49.238, + 51.354 + ], + [ + 49.031, + 50.974 + ], + [ + 48.573, + 51.009 + ], + [ + 48.426, + 51.229 + ], + [ + 47.749, + 51.337 + ], + [ + 47.688, + 51.683 + ], + [ + 47.555, + 51.736 + ], + [ + 47.579, + 51.265 + ], + [ + 47.82, + 51.264 + ], + [ + 48.109, + 50.756 + ], + [ + 48.255, + 50.938 + ], + [ + 48.317, + 50.81 + ], + [ + 48.257, + 50.612 + ], + [ + 47.989, + 50.829 + ], + [ + 47.88, + 50.702 + ], + [ + 48.39, + 50.121 + ], + [ + 48.04, + 50.32 + ], + [ + 47.965, + 49.886 + ], + [ + 47.88, + 50.304 + ], + [ + 47.664, + 50.593 + ], + [ + 47.806, + 50.702 + ], + [ + 47.698, + 50.864 + ], + [ + 47.334, + 50.792 + ], + [ + 47.286, + 50.504 + ], + [ + 46.875, + 50.503 + ], + [ + 46.791, + 50.304 + ], + [ + 46.865, + 50.195 + ], + [ + 46.67, + 50.12 + ], + [ + 46.742, + 50.774 + ], + [ + 47.068, + 50.575 + ], + [ + 47.324, + 50.992 + ], + [ + 47.177, + 51.209 + ], + [ + 47.373, + 51.574 + ], + [ + 47.253, + 51.753 + ], + [ + 47.492, + 51.937 + ], + [ + 47.19, + 52.172 + ], + [ + 46.972, + 51.919 + ], + [ + 46.657, + 52.029 + ], + [ + 46.536, + 51.701 + ], + [ + 46.621, + 52.118 + ], + [ + 46.355, + 52.263 + ], + [ + 46.561, + 52.427 + ], + [ + 46.705, + 52.352 + ], + [ + 46.706, + 52.606 + ], + [ + 46.296, + 52.682 + ], + [ + 46.1, + 52.354 + ], + [ + 46.016, + 52.409 + ], + [ + 46.259, + 52.699 + ], + [ + 46.148, + 52.861 + ], + [ + 46.45, + 52.918 + ], + [ + 46.233, + 53.242 + ], + [ + 46.246, + 53.443 + ], + [ + 46.004, + 53.369 + ], + [ + 45.919, + 53.606 + ], + [ + 46.137, + 54.113 + ], + [ + 46.003, + 54.676 + ], + [ + 44.719, + 53.37 + ], + [ + 44.211, + 53.044 + ], + [ + 42.732, + 52.426 + ], + [ + 42.103, + 52.426 + ], + [ + 41.836, + 52.825 + ], + [ + 41.727, + 52.626 + ], + [ + 41.983, + 52.135 + ], + [ + 42.151, + 52.28 + ], + [ + 43.025, + 52.245 + ], + [ + 44.067, + 52.791 + ], + [ + 43.873, + 52.209 + ], + [ + 43.41, + 52.136 + ], + [ + 43.255, + 51.936 + ], + [ + 43.436, + 51.701 + ], + [ + 43.001, + 51.954 + ], + [ + 42.2, + 51.807 + ], + [ + 42.03, + 52.063 + ], + [ + 41.679, + 51.863 + ], + [ + 41.885, + 51.628 + ], + [ + 42.031, + 51.665 + ], + [ + 42.054, + 51.482 + ], + [ + 42.261, + 51.464 + ], + [ + 42.285, + 51.32 + ], + [ + 42.115, + 51.173 + ], + [ + 42.262, + 50.992 + ], + [ + 42.115, + 50.883 + ], + [ + 42.2, + 50.611 + ], + [ + 42.49, + 50.901 + ], + [ + 42.757, + 50.792 + ], + [ + 42.732, + 50.611 + ], + [ + 42.951, + 50.647 + ], + [ + 43.193, + 50.139 + ], + [ + 43.399, + 50.011 + ], + [ + 43.701, + 49.086 + ], + [ + 44.004, + 49.068 + ], + [ + 44.067, + 48.65 + ], + [ + 43.726, + 48.58 + ], + [ + 43.956, + 48.777 + ], + [ + 43.896, + 49.051 + ], + [ + 43.605, + 48.942 + ], + [ + 43.412, + 49.377 + ], + [ + 43.049, + 49.704 + ], + [ + 42.829, + 49.667 + ], + [ + 42.868, + 50.048 + ], + [ + 42.539, + 50.283 + ], + [ + 42.297, + 50.14 + ], + [ + 42.298, + 50.357 + ], + [ + 42.177, + 50.285 + ], + [ + 42.115, + 49.829 + ], + [ + 42.007, + 49.776 + ], + [ + 42.093, + 50.231 + ], + [ + 41.945, + 50.266 + ], + [ + 42.042, + 50.375 + ], + [ + 41.739, + 51.011 + ], + [ + 41.644, + 51.046 + ], + [ + 41.618, + 50.865 + ], + [ + 41.424, + 50.975 + ], + [ + 41.352, + 50.757 + ], + [ + 40.625, + 50.683 + ], + [ + 41.304, + 50.974 + ], + [ + 41.389, + 51.281 + ], + [ + 41.559, + 51.392 + ], + [ + 41.085, + 52.099 + ], + [ + 40.165, + 52.136 + ], + [ + 39.827, + 51.991 + ], + [ + 39.099, + 52.389 + ], + [ + 38.847, + 52.226 + ], + [ + 38.905, + 51.989 + ], + [ + 38.227, + 51.483 + ], + [ + 38.203, + 51.155 + ], + [ + 37.73, + 50.884 + ], + [ + 37.779, + 50.703 + ], + [ + 37.537, + 50.193 + ], + [ + 36.881, + 49.36 + ], + [ + 36.981, + 49.177 + ], + [ + 36.859, + 48.924 + ], + [ + 36.884, + 48.633 + ], + [ + 37.028, + 48.488 + ], + [ + 36.969, + 47.998 + ], + [ + 36.835, + 48.198 + ], + [ + 36.907, + 48.416 + ], + [ + 36.786, + 48.74 + ], + [ + 36.811, + 49.216 + ], + [ + 36.604, + 49.558 + ], + [ + 35.927, + 49.195 + ], + [ + 34.789, + 49.197 + ], + [ + 34.497, + 49.051 + ], + [ + 34.424, + 49.197 + ], + [ + 33.503, + 48.942 + ], + [ + 33.431, + 48.542 + ], + [ + 33.093, + 48.725 + ], + [ + 32.802, + 48.506 + ], + [ + 32.679, + 48.87 + ], + [ + 32.534, + 48.869 + ], + [ + 31.264, + 47.873 + ], + [ + 31.638, + 47.309 + ], + [ + 31.905, + 47.344 + ], + [ + 31.881, + 47.163 + ], + [ + 32.208, + 47.001 + ], + [ + 32.039, + 46.964 + ], + [ + 32.05, + 46.618 + ], + [ + 31.736, + 47.237 + ], + [ + 30.984, + 47.706 + ], + [ + 30.657, + 47.11 + ], + [ + 30.608, + 46.638 + ], + [ + 30.488, + 46.42 + ], + [ + 30.524, + 46.039 + ], + [ + 30.198, + 46.31 + ], + [ + 30.318, + 46.71 + ], + [ + 30.55, + 46.727 + ], + [ + 30.61, + 47.001 + ], + [ + 30.271, + 47.146 + ], + [ + 30.658, + 47.216 + ], + [ + 30.875, + 47.762 + ], + [ + 30.717, + 47.963 + ], + [ + 30.233, + 48.217 + ], + [ + 30.016, + 48.109 + ], + [ + 29.045, + 48.361 + ], + [ + 28.44, + 48.035 + ], + [ + 27.957, + 47.999 + ], + [ + 27.352, + 48.323 + ], + [ + 27.714, + 47.636 + ], + [ + 27.521, + 47.816 + ], + [ + 26.429, + 46.545 + ], + [ + 26.078, + 46.347 + ], + [ + 26.164, + 45.784 + ], + [ + 26.285, + 45.747 + ], + [ + 26.333, + 45.929 + ], + [ + 26.516, + 45.693 + ], + [ + 26.271, + 45.659 + ], + [ + 26.465, + 45.403 + ], + [ + 26.018, + 45.676 + ], + [ + 25.569, + 45.369 + ], + [ + 25.654, + 45.24 + ], + [ + 26.055, + 45.295 + ], + [ + 26.081, + 45.151 + ], + [ + 25.873, + 45.13 + ], + [ + 25.788, + 44.896 + ], + [ + 25.872, + 44.476 + ], + [ + 25.63, + 44.84 + ], + [ + 25.269, + 44.767 + ], + [ + 25.134, + 44.642 + ], + [ + 25.11, + 44.276 + ], + [ + 24.746, + 43.987 + ], + [ + 24.869, + 43.734 + ], + [ + 24.699, + 43.806 + ], + [ + 24.711, + 43.353 + ], + [ + 24.59, + 43.752 + ], + [ + 24.08, + 43.68 + ], + [ + 23.971, + 43.624 + ], + [ + 24.092, + 43.443 + ], + [ + 23.887, + 43.426 + ], + [ + 23.839, + 43.17 + ], + [ + 23.729, + 43.443 + ], + [ + 23.765, + 43.754 + ], + [ + 23.475, + 43.642 + ], + [ + 23.45, + 43.461 + ], + [ + 23.318, + 43.553 + ], + [ + 23.572, + 43.898 + ], + [ + 23.873, + 43.916 + ], + [ + 23.535, + 44.132 + ], + [ + 23.634, + 45.076 + ], + [ + 23.839, + 45.532 + ], + [ + 23.401, + 45.423 + ], + [ + 23.185, + 45.601 + ], + [ + 22.023, + 45.746 + ], + [ + 22.24, + 45.567 + ], + [ + 23.21, + 45.53 + ], + [ + 23.292, + 45.439 + ], + [ + 22.747, + 45.458 + ], + [ + 22.675, + 45.239 + ], + [ + 22.53, + 45.493 + ], + [ + 22.239, + 45.495 + ], + [ + 22.264, + 45.347 + ], + [ + 21.998, + 45.167 + ], + [ + 22.131, + 45.548 + ], + [ + 21.902, + 45.895 + ], + [ + 21.441, + 45.421 + ], + [ + 20.616, + 45.132 + ], + [ + 21.196, + 45.203 + ], + [ + 21.816, + 45.622 + ], + [ + 21.319, + 45.167 + ], + [ + 21.247, + 44.914 + ], + [ + 21.052, + 44.985 + ], + [ + 21.077, + 44.66 + ], + [ + 20.908, + 44.879 + ], + [ + 20.811, + 44.442 + ], + [ + 20.725, + 44.714 + ], + [ + 20.872, + 44.968 + ], + [ + 20.349, + 45.168 + ], + [ + 20.181, + 44.804 + ], + [ + 19.804, + 44.64 + ], + [ + 19.949, + 44.459 + ], + [ + 19.756, + 44.424 + ], + [ + 20.048, + 44.279 + ], + [ + 19.648, + 44.115 + ], + [ + 19.563, + 43.877 + ], + [ + 19.672, + 43.716 + ], + [ + 19.38, + 43.716 + ], + [ + 19.113, + 43.498 + ], + [ + 18.897, + 42.916 + ], + [ + 18.896, + 43.571 + ], + [ + 18.509, + 43.714 + ], + [ + 18.217, + 43.568 + ], + [ + 18.122, + 43.279 + ], + [ + 18.037, + 43.408 + ], + [ + 18.195, + 43.751 + ], + [ + 18.461, + 43.824 + ], + [ + 19.091, + 43.68 + ], + [ + 19.636, + 44.314 + ], + [ + 19.877, + 45.077 + ], + [ + 19.381, + 45.094 + ], + [ + 18.193, + 45.894 + ], + [ + 18.205, + 45.657 + ], + [ + 18.641, + 45.15 + ], + [ + 18.267, + 45.384 + ], + [ + 18.013, + 44.749 + ], + [ + 18.134, + 44.568 + ], + [ + 17.541, + 44.043 + ], + [ + 17.844, + 44.605 + ], + [ + 17.479, + 44.64 + ], + [ + 17.575, + 44.861 + ], + [ + 17.431, + 45.077 + ], + [ + 17.444, + 45.313 + ], + [ + 17.154, + 44.877 + ], + [ + 17.165, + 45.003 + ], + [ + 17.672, + 45.695 + ], + [ + 17.66, + 45.891 + ], + [ + 17.781, + 46.001 + ], + [ + 17.856, + 45.785 + ], + [ + 18.037, + 46.021 + ], + [ + 17.71, + 46.219 + ], + [ + 17.951, + 46.366 + ], + [ + 18.315, + 46.255 + ], + [ + 18.594, + 46.458 + ], + [ + 18.423, + 46.564 + ], + [ + 18.327, + 47.255 + ], + [ + 18.097, + 47.346 + ], + [ + 18.086, + 47.618 + ], + [ + 17.904, + 47.781 + ], + [ + 17.565, + 48.034 + ], + [ + 17.04, + 47.888 + ], + [ + 16.991, + 47.398 + ], + [ + 16.911, + 47.344 + ], + [ + 16.994, + 47.836 + ], + [ + 16.791, + 48.18 + ], + [ + 15.432, + 48.324 + ], + [ + 15.093, + 48.215 + ], + [ + 15.118, + 48.07 + ], + [ + 14.972, + 48.217 + ], + [ + 14.656, + 48.18 + ], + [ + 14.439, + 47.852 + ], + [ + 13.156, + 47.927 + ], + [ + 12.671, + 48.108 + ], + [ + 12.236, + 47.999 + ], + [ + 12.345, + 47.471 + ], + [ + 12.076, + 46.926 + ], + [ + 12.526, + 46.62 + ], + [ + 12.986, + 46.618 + ], + [ + 13.07, + 46.346 + ], + [ + 12.828, + 44.786 + ], + [ + 12.32, + 43.261 + ], + [ + 11.787, + 42.211 + ], + [ + 10.466, + 40.413 + ], + [ + 9.959, + 39.903 + ], + [ + 9.255, + 39.685 + ], + [ + 9.231, + 39.47 + ], + [ + 8.638, + 39.028 + ], + [ + 7.547, + 38.666 + ], + [ + 7.643, + 37.903 + ], + [ + 7.535, + 37.957 + ], + [ + 7.584, + 38.285 + ], + [ + 7.45, + 38.629 + ], + [ + 7.037, + 38.665 + ], + [ + 6.918, + 39.028 + ], + [ + 6.626, + 39.029 + ], + [ + 5.874, + 38.81 + ], + [ + 5.755, + 38.447 + ], + [ + 5.052, + 38.34 + ], + [ + 4.76, + 37.865 + ], + [ + 4.397, + 38.119 + ], + [ + 2.921, + 37.975 + ], + [ + 2.533, + 37.685 + ], + [ + 2.023, + 37.687 + ], + [ + 1.673, + 37.341 + ], + [ + 2.641, + 37.124 + ], + [ + 2.398, + 36.761 + ], + [ + 2.363, + 36.196 + ], + [ + 2.896, + 36.416 + ], + [ + 3.089, + 36.197 + ], + [ + 3.404, + 36.415 + ], + [ + 3.585, + 36.326 + ], + [ + 3.718, + 35.943 + ], + [ + 4.784, + 36.088 + ], + [ + 4.808, + 35.834 + ], + [ + 5.1, + 36.017 + ], + [ + 6.407, + 35.472 + ], + [ + 7.039, + 35.691 + ], + [ + 7.475, + 35.182 + ], + [ + 7.814, + 35.29 + ], + [ + 7.934, + 35.108 + ], + [ + 8.516, + 35.072 + ], + [ + 8.902, + 35.291 + ], + [ + 9.267, + 34.89 + ], + [ + 9.581, + 34.783 + ], + [ + 10.647, + 34.71 + ], + [ + 10.915, + 34.347 + ], + [ + 11.738, + 34.456 + ], + [ + 12.488, + 34.273 + ], + [ + 12.778, + 34.529 + ], + [ + 13.166, + 34.565 + ], + [ + 13.36, + 35.072 + ], + [ + 13.41, + 34.745 + ], + [ + 13.627, + 34.709 + ], + [ + 13.7, + 34.963 + ], + [ + 14.355, + 35.109 + ], + [ + 14.729, + 34.51 + ], + [ + 14.923, + 33.892 + ], + [ + 14.778, + 33.529 + ], + [ + 15.239, + 33.24 + ], + [ + 15.069, + 32.26 + ], + [ + 14.719, + 32.241 + ], + [ + 14.487, + 32.041 + ], + [ + 14.608, + 31.387 + ], + [ + 14.27, + 30.554 + ], + [ + 13.965, + 30.425 + ], + [ + 13.895, + 30.209 + ], + [ + 12.272, + 30.062 + ], + [ + 12.357, + 29.791 + ], + [ + 12.054, + 29.519 + ], + [ + 11.737, + 29.591 + ], + [ + 11.058, + 29.337 + ], + [ + 11.011, + 29.157 + ], + [ + 10.55, + 29.158 + ], + [ + 10.622, + 28.721 + ], + [ + 10.395, + 28.775 + ], + [ + 10.552, + 28.939 + ], + [ + 9.51, + 28.685 + ], + [ + 8.588, + 28.974 + ], + [ + 8.529, + 29.211 + ], + [ + 8.771, + 29.393 + ], + [ + 8.722, + 29.573 + ], + [ + 8.297, + 29.808 + ], + [ + 8.214, + 30.263 + ], + [ + 8.02, + 30.518 + ], + [ + 8.067, + 30.698 + ], + [ + 7.438, + 31.244 + ], + [ + 7.401, + 31.734 + ], + [ + 6.991, + 31.733 + ], + [ + 6.832, + 31.353 + ], + [ + 7.1, + 31.205 + ], + [ + 6.953, + 30.952 + ], + [ + 7.124, + 30.882 + ], + [ + 7.027, + 30.772 + ], + [ + 7.05, + 29.862 + ], + [ + 6.784, + 29.499 + ], + [ + 6.856, + 29.248 + ], + [ + 6.724, + 28.939 + ], + [ + 6.445, + 28.957 + ], + [ + 6.625, + 28.648 + ], + [ + 7.233, + 28.537 + ], + [ + 7.22, + 28.05 + ], + [ + 6.869, + 27.777 + ], + [ + 6.24, + 28.175 + ], + [ + 5.682, + 28.104 + ], + [ + 5.731, + 27.922 + ], + [ + 5.367, + 27.703 + ], + [ + 5.149, + 27.743 + ], + [ + 5.075, + 27.995 + ], + [ + 4.908, + 27.959 + ], + [ + 5.161, + 27.105 + ], + [ + 4.774, + 26.78 + ], + [ + 5.295, + 26.833 + ], + [ + 5.538, + 26.542 + ], + [ + 5.631, + 26.761 + ], + [ + 5.946, + 26.833 + ], + [ + 5.718, + 26.852 + ], + [ + 5.851, + 27.013 + ], + [ + 6.092, + 27.016 + ], + [ + 6.553, + 26.688 + ], + [ + 6.541, + 26.126 + ], + [ + 6.396, + 26.054 + ], + [ + 6.397, + 25.365 + ], + [ + 6.227, + 25.11 + ], + [ + 6.492, + 24.819 + ], + [ + 6.482, + 24.656 + ], + [ + 6.748, + 24.656 + ], + [ + 6.965, + 24.331 + ], + [ + 7.594, + 24.111 + ], + [ + 7.56, + 24.493 + ], + [ + 7.316, + 24.891 + ], + [ + 7.292, + 25.473 + ], + [ + 6.979, + 25.98 + ], + [ + 7.135, + 26.291 + ], + [ + 7.451, + 26.255 + ], + [ + 7.473, + 26.651 + ], + [ + 7.909, + 26.434 + ], + [ + 7.824, + 26.272 + ], + [ + 7.91, + 26.106 + ], + [ + 8.478, + 26.524 + ], + [ + 8.273, + 26.835 + ], + [ + 9.267, + 26.616 + ], + [ + 9.606, + 26.399 + ], + [ + 9.679, + 26.181 + ], + [ + 9.921, + 26.362 + ], + [ + 10.962, + 26.288 + ], + [ + 10.925, + 26.561 + ], + [ + 11.146, + 26.78 + ], + [ + 11.107, + 26.943 + ], + [ + 11.666, + 27.087 + ], + [ + 12.514, + 26.904 + ], + [ + 12.878, + 26.615 + ], + [ + 13.288, + 26.615 + ], + [ + 14.016, + 26.107 + ], + [ + 15.105, + 26.181 + ], + [ + 15.384, + 26.38 + ], + [ + 15.359, + 26.67 + ], + [ + 14.863, + 26.471 + ], + [ + 14.803, + 27.031 + ], + [ + 14.705, + 27.105 + ], + [ + 14.983, + 27.123 + ], + [ + 15.65, + 27.578 + ], + [ + 15.276, + 27.813 + ], + [ + 15.685, + 27.741 + ], + [ + 15.952, + 28.103 + ], + [ + 16.219, + 28.141 + ], + [ + 16.437, + 28.102 + ], + [ + 16.657, + 27.813 + ], + [ + 17.044, + 28.575 + ], + [ + 17.019, + 28.247 + ], + [ + 17.769, + 28.357 + ], + [ + 17.841, + 28.504 + ], + [ + 17.916, + 28.357 + ], + [ + 18.545, + 28.358 + ], + [ + 18.605, + 28.23 + ], + [ + 17.09, + 28.175 + ], + [ + 16.947, + 27.778 + ], + [ + 16.751, + 27.669 + ], + [ + 16.17, + 28.032 + ], + [ + 15.759, + 27.414 + ], + [ + 14.983, + 26.977 + ], + [ + 14.959, + 26.723 + ], + [ + 15.443, + 26.907 + ], + [ + 15.904, + 26.653 + ], + [ + 16.085, + 26.451 + ], + [ + 16.049, + 26.287 + ], + [ + 16.607, + 26.435 + ], + [ + 16.728, + 26.251 + ], + [ + 16.922, + 26.397 + ], + [ + 17.067, + 26.216 + ], + [ + 17.735, + 26.126 + ], + [ + 16.487, + 26.106 + ], + [ + 16.243, + 25.998 + ], + [ + 16.062, + 25.69 + ], + [ + 16.438, + 25.49 + ], + [ + 16.51, + 25.381 + ], + [ + 16.05, + 25.563 + ], + [ + 15.928, + 25.998 + ], + [ + 15.493, + 25.6 + ], + [ + 14.923, + 25.438 + ], + [ + 15.056, + 25.198 + ], + [ + 15.663, + 24.982 + ], + [ + 15.856, + 24.8 + ], + [ + 15.179, + 25.127 + ], + [ + 14.862, + 25.089 + ], + [ + 14.67, + 25.489 + ], + [ + 13.3, + 25.29 + ], + [ + 13.652, + 24.947 + ], + [ + 13.845, + 25.018 + ], + [ + 13.688, + 24.856 + ], + [ + 13.773, + 24.728 + ], + [ + 14.136, + 24.729 + ], + [ + 14.269, + 24.637 + ], + [ + 13.869, + 24.691 + ], + [ + 13.845, + 24.437 + ], + [ + 14.79, + 24.148 + ], + [ + 14.946, + 24.021 + ], + [ + 14.621, + 23.892 + ], + [ + 14.644, + 23.785 + ], + [ + 15.251, + 23.713 + ], + [ + 15.347, + 23.529 + ], + [ + 14.96, + 23.64 + ], + [ + 14.789, + 23.531 + ], + [ + 14.692, + 23.711 + ], + [ + 14.498, + 23.566 + ], + [ + 14.283, + 23.639 + ], + [ + 14.197, + 23.875 + ], + [ + 14.366, + 23.984 + ], + [ + 14.258, + 24.147 + ], + [ + 13.532, + 24.291 + ], + [ + 13.469, + 23.912 + ], + [ + 13.772, + 23.384 + ], + [ + 13.095, + 24.11 + ], + [ + 12.803, + 24.111 + ], + [ + 12.815, + 23.911 + ], + [ + 13.022, + 23.82 + ], + [ + 13.167, + 23.529 + ], + [ + 12.562, + 24.003 + ], + [ + 12.33, + 24.385 + ], + [ + 12.405, + 24.566 + ], + [ + 12.332, + 24.783 + ], + [ + 12.175, + 24.91 + ], + [ + 11.024, + 24.891 + ], + [ + 11.277, + 24.436 + ], + [ + 11.641, + 24.183 + ], + [ + 10.283, + 24.835 + ], + [ + 9.909, + 24.783 + ], + [ + 10.054, + 24.673 + ], + [ + 10.064, + 24.33 + ], + [ + 10.441, + 24.094 + ], + [ + 10.199, + 23.949 + ], + [ + 10.442, + 23.477 + ], + [ + 10.404, + 23.312 + ], + [ + 10.939, + 22.987 + ], + [ + 11.495, + 22.222 + ], + [ + 12.174, + 21.969 + ], + [ + 12.705, + 21.534 + ], + [ + 13.215, + 21.46 + ], + [ + 14.403, + 20.626 + ], + [ + 14.428, + 20.481 + ], + [ + 13.071, + 21.316 + ], + [ + 12.632, + 21.245 + ], + [ + 12.368, + 21.645 + ], + [ + 12.173, + 21.643 + ], + [ + 12.186, + 21.479 + ], + [ + 11.957, + 21.279 + ], + [ + 12.113, + 21.733 + ], + [ + 11.666, + 21.824 + ], + [ + 11.471, + 22.116 + ], + [ + 10.89, + 21.823 + ], + [ + 10.551, + 21.859 + ], + [ + 10.187, + 22.514 + ], + [ + 9.993, + 22.405 + ], + [ + 9.775, + 22.514 + ], + [ + 9.836, + 22.388 + ], + [ + 9.703, + 22.477 + ], + [ + 9.765, + 22.315 + ], + [ + 9.654, + 22.406 + ], + [ + 9.678, + 22.262 + ], + [ + 9.34, + 22.551 + ], + [ + 9.048, + 22.44 + ], + [ + 9.024, + 22.66 + ], + [ + 8.711, + 22.514 + ], + [ + 8.661, + 22.258 + ], + [ + 8.661, + 22.625 + ], + [ + 8.468, + 22.586 + ], + [ + 7.618, + 23.494 + ], + [ + 7.061, + 23.386 + ], + [ + 6.893, + 23.675 + ], + [ + 6.724, + 23.604 + ], + [ + 6.506, + 23.894 + ], + [ + 6.141, + 23.967 + ], + [ + 5.389, + 24.475 + ], + [ + 5.247, + 24.439 + ], + [ + 5.22, + 24.257 + ], + [ + 4.834, + 24.365 + ], + [ + 4.52, + 24.003 + ], + [ + 4.252, + 24.077 + ], + [ + 4.035, + 23.819 + ], + [ + 3.355, + 23.603 + ], + [ + 2.775, + 23.857 + ], + [ + 2.714, + 24.02 + ], + [ + 2.811, + 24.347 + ], + [ + 2.507, + 24.691 + ], + [ + 1.66, + 24.581 + ], + [ + 1.2, + 24.8 + ], + [ + 0.667, + 24.691 + ], + [ + 0.581, + 24.347 + ], + [ + 0.728, + 23.982 + ], + [ + 0.363, + 23.658 + ], + [ + 0.425, + 23.386 + ], + [ + 0.799, + 23.332 + ], + [ + 0.231, + 23.312 + ], + [ + 0.17, + 23.04 + ], + [ + 0.715, + 23.132 + ], + [ + 0.971, + 22.857 + ], + [ + 0.97, + 22.604 + ], + [ + 0.824, + 22.461 + ], + [ + 0.922, + 22.385 + ], + [ + 0.667, + 22.332 + ], + [ + 0.558, + 22.061 + ], + [ + 0.605, + 21.481 + ], + [ + 0.279, + 21.426 + ], + [ + 0.256, + 21.172 + ], + [ + 0.0, + 21.006 + ], + [ + 0.388, + 20.355 + ], + [ + 0.266, + 20.103 + ], + [ + 0.533, + 19.703 + ], + [ + 0.34, + 19.665 + ], + [ + 0.702, + 19.159 + ], + [ + 0.715, + 18.848 + ], + [ + 0.837, + 18.884 + ], + [ + 1.005, + 18.631 + ], + [ + 1.162, + 18.723 + ], + [ + 1.103, + 18.958 + ], + [ + 1.248, + 18.521 + ], + [ + 1.818, + 18.432 + ], + [ + 1.272, + 18.377 + ], + [ + 0.981, + 18.558 + ], + [ + 0.677, + 18.177 + ], + [ + 0.716, + 18.014 + ], + [ + 0.535, + 17.924 + ], + [ + 0.677, + 17.851 + ], + [ + 0.484, + 17.414 + ], + [ + 0.582, + 17.199 + ], + [ + 0.508, + 17.051 + ], + [ + 0.8, + 16.835 + ], + [ + 0.752, + 16.546 + ], + [ + 0.971, + 16.363 + ], + [ + 0.872, + 16.325 + ], + [ + 0.995, + 16.144 + ], + [ + 0.872, + 15.925 + ], + [ + 1.126, + 15.838 + ], + [ + 1.296, + 15.981 + ], + [ + 1.259, + 15.782 + ], + [ + 1.357, + 15.673 + ], + [ + 1.258, + 15.419 + ], + [ + 1.732, + 15.112 + ], + [ + 1.926, + 14.747 + ], + [ + 2.266, + 14.712 + ], + [ + 2.264, + 14.893 + ], + [ + 2.483, + 14.819 + ], + [ + 2.581, + 15.038 + ], + [ + 2.724, + 14.893 + ], + [ + 2.592, + 14.764 + ], + [ + 2.689, + 14.403 + ], + [ + 2.581, + 14.167 + ], + [ + 2.943, + 14.274 + ], + [ + 3.185, + 14.132 + ], + [ + 3.175, + 14.328 + ], + [ + 3.284, + 14.382 + ], + [ + 3.514, + 14.259 + ], + [ + 3.574, + 14.021 + ], + [ + 4.131, + 14.203 + ], + [ + 4.81, + 13.912 + ], + [ + 5.755, + 14.203 + ], + [ + 5.973, + 14.818 + ], + [ + 6.383, + 14.71 + ], + [ + 6.02, + 14.529 + ], + [ + 5.875, + 14.095 + ], + [ + 5.306, + 13.931 + ], + [ + 5.572, + 13.64 + ], + [ + 5.559, + 13.476 + ], + [ + 5.404, + 13.459 + ], + [ + 5.525, + 13.278 + ], + [ + 5.452, + 13.023 + ], + [ + 5.706, + 12.606 + ], + [ + 6.058, + 12.914 + ], + [ + 5.851, + 13.186 + ], + [ + 7.329, + 13.005 + ], + [ + 7.838, + 13.26 + ], + [ + 7.231, + 12.824 + ], + [ + 6.869, + 12.787 + ], + [ + 6.964, + 12.424 + ], + [ + 6.566, + 12.515 + ], + [ + 6.663, + 12.262 + ], + [ + 6.493, + 12.153 + ], + [ + 6.638, + 12.043 + ], + [ + 6.589, + 11.825 + ], + [ + 7.306, + 11.843 + ], + [ + 7.147, + 11.428 + ], + [ + 7.376, + 11.261 + ], + [ + 7.485, + 11.317 + ], + [ + 7.39, + 11.535 + ], + [ + 7.547, + 11.624 + ], + [ + 8.079, + 11.409 + ], + [ + 8.237, + 11.571 + ], + [ + 8.164, + 11.789 + ], + [ + 8.273, + 11.953 + ], + [ + 9.606, + 11.733 + ], + [ + 10.164, + 11.19 + ], + [ + 10.332, + 11.19 + ], + [ + 10.405, + 11.479 + ], + [ + 10.6, + 11.155 + ], + [ + 11.253, + 11.261 + ], + [ + 10.526, + 10.862 + ], + [ + 10.417, + 10.518 + ], + [ + 11.638, + 10.464 + ], + [ + 12.174, + 9.956 + ], + [ + 13.046, + 9.52 + ], + [ + 13.553, + 9.665 + ], + [ + 13.495, + 9.938 + ], + [ + 13.602, + 10.028 + ], + [ + 14.014, + 9.774 + ], + [ + 14.136, + 10.028 + ], + [ + 14.451, + 10.101 + ], + [ + 14.512, + 10.301 + ], + [ + 14.367, + 10.446 + ], + [ + 14.389, + 10.63 + ], + [ + 13.859, + 10.954 + ], + [ + 14.101, + 11.028 + ], + [ + 13.931, + 11.207 + ], + [ + 13.99, + 11.335 + ], + [ + 14.329, + 11.481 + ], + [ + 14.535, + 11.353 + ], + [ + 14.401, + 11.189 + ], + [ + 14.863, + 11.081 + ], + [ + 15.128, + 10.754 + ], + [ + 15.881, + 10.754 + ], + [ + 16.46, + 10.538 + ], + [ + 16.584, + 10.535 + ], + [ + 16.425, + 10.662 + ], + [ + 16.63, + 10.792 + ], + [ + 17.357, + 10.645 + ], + [ + 16.644, + 10.373 + ], + [ + 16.896, + 9.884 + ], + [ + 17.744, + 9.122 + ], + [ + 17.892, + 9.229 + ], + [ + 19.224, + 8.758 + ], + [ + 20.046, + 8.868 + ], + [ + 20.193, + 8.757 + ], + [ + 20.349, + 8.849 + ], + [ + 20.335, + 9.157 + ], + [ + 20.482, + 8.902 + ], + [ + 20.578, + 9.122 + ], + [ + 20.748, + 8.866 + ], + [ + 20.784, + 9.139 + ], + [ + 21.136, + 9.304 + ], + [ + 21.086, + 9.449 + ], + [ + 21.354, + 9.667 + ], + [ + 21.076, + 9.174 + ], + [ + 21.56, + 9.175 + ], + [ + 21.367, + 9.031 + ], + [ + 21.51, + 8.485 + ], + [ + 21.936, + 8.394 + ], + [ + 22.033, + 7.997 + ], + [ + 22.616, + 8.032 + ], + [ + 22.93, + 7.344 + ], + [ + 23.061, + 7.396 + ], + [ + 22.917, + 7.687 + ], + [ + 22.991, + 7.943 + ], + [ + 23.208, + 8.076 + ], + [ + 23.414, + 7.876 + ], + [ + 23.404, + 7.492 + ], + [ + 23.741, + 7.421 + ], + [ + 23.656, + 7.584 + ], + [ + 23.815, + 7.639 + ], + [ + 24.021, + 7.476 + ], + [ + 24.093, + 7.802 + ], + [ + 23.874, + 8.708 + ], + [ + 24.359, + 9.18 + ], + [ + 24.067, + 9.109 + ], + [ + 24.093, + 9.616 + ], + [ + 23.971, + 9.797 + ], + [ + 24.516, + 9.708 + ], + [ + 24.6, + 9.871 + ], + [ + 24.48, + 10.015 + ], + [ + 24.577, + 10.379 + ], + [ + 24.467, + 10.978 + ], + [ + 24.832, + 10.361 + ], + [ + 25.122, + 10.47 + ], + [ + 25.389, + 9.708 + ], + [ + 25.775, + 9.962 + ], + [ + 26.115, + 9.815 + ], + [ + 26.345, + 10.053 + ], + [ + 25.91, + 10.305 + ], + [ + 26.005, + 10.706 + ], + [ + 26.26, + 11.088 + ], + [ + 27.23, + 11.558 + ], + [ + 27.351, + 12.066 + ], + [ + 27.631, + 11.76 + ], + [ + 27.073, + 9.835 + ], + [ + 27.325, + 9.779 + ], + [ + 27.388, + 10.234 + ], + [ + 27.57, + 10.359 + ], + [ + 27.532, + 9.434 + ], + [ + 27.314, + 9.255 + ], + [ + 27.495, + 8.872 + ], + [ + 27.775, + 8.71 + ], + [ + 27.738, + 8.327 + ], + [ + 27.884, + 8.328 + ], + [ + 28.052, + 8.037 + ], + [ + 28.187, + 8.491 + ], + [ + 28.32, + 8.545 + ], + [ + 28.479, + 8.165 + ], + [ + 28.392, + 7.857 + ], + [ + 28.732, + 8.11 + ], + [ + 28.975, + 8.037 + ], + [ + 29.336, + 8.546 + ], + [ + 30.161, + 8.22 + ], + [ + 31.228, + 8.364 + ], + [ + 31.36, + 8.454 + ], + [ + 31.324, + 8.619 + ], + [ + 31.809, + 9.017 + ], + [ + 32.245, + 8.981 + ], + [ + 32.413, + 9.198 + ], + [ + 33.091, + 8.765 + ], + [ + 33.333, + 8.907 + ], + [ + 33.479, + 8.764 + ], + [ + 33.177, + 9.398 + ], + [ + 33.588, + 9.798 + ], + [ + 33.238, + 10.107 + ], + [ + 33.672, + 9.924 + ], + [ + 33.661, + 9.434 + ], + [ + 33.988, + 9.235 + ], + [ + 34.302, + 9.307 + ], + [ + 34.472, + 9.853 + ], + [ + 34.861, + 9.962 + ], + [ + 35.417, + 9.744 + ], + [ + 35.672, + 9.364 + ], + [ + 35.235, + 9.036 + ], + [ + 35.249, + 7.494 + ], + [ + 35.467, + 7.604 + ], + [ + 35.755, + 7.422 + ], + [ + 35.781, + 7.238 + ], + [ + 36.242, + 7.421 + ], + [ + 36.58, + 7.384 + ], + [ + 36.557, + 7.202 + ], + [ + 37.028, + 7.222 + ], + [ + 36.218, + 7.168 + ], + [ + 36.206, + 6.568 + ], + [ + 35.78, + 6.042 + ], + [ + 35.2, + 6.151 + ], + [ + 35.356, + 5.806 + ], + [ + 35.09, + 5.623 + ], + [ + 35.236, + 5.516 + ], + [ + 35.21, + 5.153 + ], + [ + 35.38, + 5.006 + ], + [ + 35.187, + 4.755 + ], + [ + 35.611, + 4.699 + ], + [ + 35.549, + 4.536 + ], + [ + 35.709, + 4.372 + ], + [ + 35.998, + 4.626 + ], + [ + 36.411, + 4.41 + ], + [ + 36.617, + 4.028 + ], + [ + 36.555, + 3.827 + ], + [ + 36.314, + 3.865 + ], + [ + 36.277, + 3.627 + ], + [ + 36.495, + 3.556 + ], + [ + 36.508, + 3.21 + ], + [ + 37.113, + 3.464 + ], + [ + 36.906, + 3.01 + ], + [ + 37.281, + 2.702 + ], + [ + 37.464, + 2.902 + ], + [ + 37.392, + 3.046 + ], + [ + 37.599, + 3.21 + ], + [ + 37.623, + 2.918 + ], + [ + 38.059, + 3.137 + ], + [ + 37.95, + 2.939 + ], + [ + 38.022, + 2.757 + ], + [ + 38.3, + 2.665 + ], + [ + 38.529, + 3.194 + ], + [ + 38.93, + 3.609 + ], + [ + 39.779, + 3.427 + ], + [ + 39.837, + 3.554 + ], + [ + 39.584, + 3.755 + ], + [ + 39.438, + 4.153 + ], + [ + 39.33, + 4.138 + ], + [ + 39.367, + 4.407 + ], + [ + 40.77, + 4.226 + ], + [ + 40.903, + 4.099 + ], + [ + 40.868, + 3.609 + ], + [ + 41.014, + 3.683 + ], + [ + 41.691, + 3.356 + ], + [ + 41.959, + 3.429 + ], + [ + 42.83, + 2.591 + ], + [ + 43.485, + 2.739 + ], + [ + 43.702, + 2.957 + ], + [ + 43.969, + 2.414 + ], + [ + 44.185, + 2.375 + ], + [ + 44.186, + 2.629 + ], + [ + 44.478, + 2.774 + ], + [ + 44.732, + 2.431 + ], + [ + 44.743, + 2.157 + ], + [ + 45.312, + 1.523 + ], + [ + 45.253, + 1.359 + ], + [ + 45.641, + 1.213 + ], + [ + 45.845, + 1.414 + ], + [ + 46.162, + 2.213 + ], + [ + 45.799, + 2.721 + ], + [ + 46.307, + 2.685 + ], + [ + 46.052, + 4.008 + ], + [ + 46.377, + 3.63 + ], + [ + 46.404, + 3.192 + ], + [ + 46.621, + 2.646 + ], + [ + 46.877, + 2.375 + ], + [ + 47.046, + 2.376 + ], + [ + 47.094, + 2.63 + ], + [ + 47.286, + 1.978 + ], + [ + 47.772, + 1.977 + ], + [ + 48.305, + 1.723 + ], + [ + 48.45, + 1.431 + ], + [ + 49.031, + 1.357 + ], + [ + 49.153, + 1.141 + ], + [ + 49.444, + 1.178 + ], + [ + 50.025, + 0.633 + ], + [ + 50.109, + 0.69 + ], + [ + 49.953, + 0.961 + ], + [ + 49.333, + 1.377 + ], + [ + 49.759, + 1.287 + ], + [ + 49.963, + 1.705 + ], + [ + 49.88, + 1.832 + ], + [ + 49.686, + 1.579 + ], + [ + 49.455, + 1.668 + ], + [ + 49.627, + 1.812 + ], + [ + 49.745, + 2.285 + ], + [ + 49.358, + 2.865 + ], + [ + 49.638, + 2.81 + ], + [ + 49.891, + 3.011 + ], + [ + 49.746, + 3.119 + ], + [ + 49.648, + 3.738 + ], + [ + 49.287, + 4.099 + ], + [ + 49.455, + 4.099 + ], + [ + 49.212, + 4.753 + ], + [ + 49.333, + 4.97 + ], + [ + 48.558, + 5.516 + ], + [ + 48.656, + 5.588 + ], + [ + 48.584, + 6.095 + ], + [ + 48.328, + 6.442 + ], + [ + 48.692, + 6.222 + ], + [ + 48.815, + 5.824 + ], + [ + 49.334, + 5.625 + ], + [ + 49.165, + 5.151 + ], + [ + 49.455, + 5.041 + ], + [ + 49.627, + 3.955 + ], + [ + 50.048, + 3.391 + ], + [ + 50.631, + 3.282 + ], + [ + 50.546, + 3.483 + ], + [ + 50.705, + 3.573 + ], + [ + 51.14, + 3.356 + ], + [ + 51.816, + 3.319 + ], + [ + 51.952, + 3.483 + ], + [ + 51.708, + 3.773 + ], + [ + 52.034, + 4.044 + ], + [ + 52.047, + 4.244 + ], + [ + 51.673, + 4.371 + ], + [ + 51.454, + 4.699 + ], + [ + 51.042, + 4.734 + ], + [ + 50.909, + 4.936 + ], + [ + 51.914, + 5.063 + ], + [ + 51.394, + 5.259 + ], + [ + 51.841, + 5.714 + ], + [ + 52.013, + 5.643 + ], + [ + 52.254, + 5.824 + ], + [ + 53.246, + 5.897 + ], + [ + 53.658, + 5.461 + ], + [ + 53.573, + 5.696 + ], + [ + 53.826, + 6.076 + ], + [ + 54.253, + 6.203 + ], + [ + 53.913, + 6.678 + ], + [ + 53.924, + 6.875 + ], + [ + 54.132, + 7.039 + ], + [ + 53.72, + 7.477 + ], + [ + 53.767, + 7.693 + ], + [ + 54.217, + 7.711 + ], + [ + 54.179, + 7.91 + ], + [ + 54.507, + 8.401 + ], + [ + 54.99, + 8.655 + ], + [ + 55.33, + 8.618 + ], + [ + 55.537, + 8.964 + ], + [ + 55.546, + 9.636 + ], + [ + 56.08, + 9.815 + ], + [ + 56.274, + 9.706 + ], + [ + 56.784, + 10.143 + ], + [ + 56.856, + 10.47 + ], + [ + 57.184, + 10.597 + ], + [ + 57.256, + 10.995 + ], + [ + 57.401, + 11.105 + ], + [ + 57.302, + 11.358 + ], + [ + 57.473, + 11.506 + ], + [ + 57.402, + 12.012 + ], + [ + 57.619, + 12.337 + ], + [ + 57.472, + 12.775 + ], + [ + 57.642, + 12.92 + ], + [ + 57.341, + 13.011 + ], + [ + 57.414, + 13.23 + ], + [ + 58.041, + 13.411 + ], + [ + 58.189, + 13.628 + ], + [ + 58.527, + 13.591 + ], + [ + 58.734, + 13.825 + ], + [ + 58.77, + 14.171 + ], + [ + 59.11, + 14.426 + ], + [ + 60.536, + 14.897 + ], + [ + 60.514, + 15.078 + ], + [ + 60.235, + 15.241 + ], + [ + 60.429, + 15.715 + ], + [ + 60.139, + 16.039 + ], + [ + 60.49, + 16.023 + ], + [ + 60.526, + 16.477 + ], + [ + 60.805, + 16.566 + ], + [ + 61.362, + 17.512 + ], + [ + 61.617, + 16.767 + ], + [ + 61.448, + 16.005 + ], + [ + 61.506, + 15.84 + ], + [ + 61.821, + 15.733 + ], + [ + 62.234, + 16.24 + ], + [ + 62.887, + 16.204 + ], + [ + 63.034, + 16.35 + ], + [ + 62.85, + 16.04 + ], + [ + 62.899, + 15.715 + ], + [ + 63.736, + 15.042 + ], + [ + 64.654, + 13.918 + ], + [ + 64.802, + 13.953 + ], + [ + 64.716, + 13.753 + ], + [ + 65.25, + 13.246 + ], + [ + 65.191, + 13.009 + ], + [ + 65.771, + 13.083 + ], + [ + 66.256, + 12.902 + ], + [ + 67.514, + 11.994 + ], + [ + 67.478, + 11.648 + ], + [ + 67.658, + 11.704 + ], + [ + 67.72, + 11.541 + ], + [ + 67.297, + 11.378 + ], + [ + 67.091, + 11.104 + ], + [ + 67.127, + 10.977 + ], + [ + 67.443, + 11.014 + ], + [ + 67.66, + 10.797 + ], + [ + 68.168, + 10.869 + ], + [ + 68.605, + 10.651 + ], + [ + 69.125, + 10.886 + ], + [ + 69.029, + 11.032 + ], + [ + 69.16, + 11.486 + ], + [ + 69.345, + 11.32 + ], + [ + 69.113, + 11.159 + ], + [ + 69.791, + 11.051 + ], + [ + 70.082, + 10.615 + ], + [ + 70.35, + 10.65 + ], + [ + 70.568, + 10.397 + ], + [ + 70.81, + 10.541 + ], + [ + 71.366, + 9.962 + ], + [ + 71.754, + 9.78 + ], + [ + 71.813, + 9.325 + ], + [ + 72.046, + 9.054 + ], + [ + 72.299, + 9.398 + ], + [ + 72.153, + 9.725 + ], + [ + 72.215, + 9.852 + ], + [ + 73.002, + 10.054 + ], + [ + 72.324, + 11.068 + ], + [ + 72.372, + 11.212 + ], + [ + 72.237, + 11.487 + ], + [ + 71.536, + 12.103 + ], + [ + 71.973, + 12.139 + ], + [ + 72.191, + 12.464 + ], + [ + 72.625, + 11.812 + ], + [ + 72.76, + 11.83 + ], + [ + 72.771, + 12.029 + ], + [ + 72.866, + 11.704 + ], + [ + 73.377, + 11.303 + ], + [ + 73.353, + 11.158 + ], + [ + 74.151, + 11.012 + ], + [ + 73.921, + 11.468 + ], + [ + 74.335, + 11.902 + ], + [ + 74.164, + 12.266 + ], + [ + 74.637, + 12.465 + ], + [ + 74.794, + 12.81 + ], + [ + 74.768, + 13.173 + ], + [ + 75.085, + 13.283 + ], + [ + 75.182, + 13.536 + ], + [ + 75.121, + 14.134 + ], + [ + 75.691, + 13.244 + ], + [ + 75.654, + 12.502 + ], + [ + 75.98, + 12.882 + ], + [ + 75.931, + 13.282 + ], + [ + 76.03, + 13.39 + ], + [ + 75.883, + 13.463 + ], + [ + 75.883, + 13.682 + ], + [ + 76.019, + 13.954 + ], + [ + 76.477, + 13.88 + ], + [ + 76.503, + 13.664 + ], + [ + 76.732, + 13.537 + ], + [ + 76.223, + 13.101 + ], + [ + 76.527, + 12.611 + ], + [ + 76.745, + 12.611 + ], + [ + 76.889, + 12.176 + ], + [ + 77.035, + 12.139 + ], + [ + 77.178, + 12.502 + ], + [ + 77.664, + 12.794 + ], + [ + 77.471, + 12.429 + ], + [ + 77.241, + 12.338 + ], + [ + 77.156, + 11.922 + ], + [ + 76.876, + 11.795 + ], + [ + 76.889, + 11.524 + ], + [ + 77.106, + 11.63 + ], + [ + 77.398, + 11.305 + ], + [ + 77.664, + 11.45 + ], + [ + 77.954, + 11.051 + ], + [ + 78.027, + 11.305 + ], + [ + 78.221, + 11.449 + ], + [ + 78.197, + 11.27 + ], + [ + 78.393, + 11.303 + ], + [ + 78.488, + 11.521 + ], + [ + 79.02, + 11.197 + ], + [ + 79.529, + 11.233 + ], + [ + 79.677, + 11.414 + ], + [ + 79.891, + 11.267 + ], + [ + 79.639, + 11.866 + ], + [ + 80.39, + 11.904 + ], + [ + 80.413, + 12.229 + ], + [ + 80.219, + 12.483 + ], + [ + 80.391, + 12.629 + ], + [ + 80.316, + 12.776 + ], + [ + 80.595, + 12.719 + ], + [ + 80.512, + 12.955 + ], + [ + 80.801, + 13.429 + ], + [ + 81.104, + 13.517 + ], + [ + 81.177, + 13.734 + ], + [ + 81.298, + 13.664 + ], + [ + 81.408, + 13.936 + ], + [ + 80.873, + 14.225 + ], + [ + 81.031, + 14.39 + ], + [ + 81.152, + 14.316 + ], + [ + 81.407, + 14.77 + ], + [ + 81.431, + 15.061 + ], + [ + 81.624, + 15.17 + ], + [ + 81.614, + 15.37 + ], + [ + 81.977, + 15.37 + ], + [ + 82.049, + 15.769 + ], + [ + 82.399, + 15.171 + ], + [ + 82.266, + 14.86 + ], + [ + 81.938, + 14.878 + ], + [ + 82.037, + 14.444 + ], + [ + 81.623, + 13.717 + ], + [ + 81.88, + 13.591 + ], + [ + 82.341, + 14.028 + ], + [ + 82.569, + 13.936 + ], + [ + 82.304, + 13.716 + ], + [ + 82.339, + 13.373 + ], + [ + 82.026, + 13.444 + ], + [ + 81.612, + 13.227 + ], + [ + 81.275, + 13.372 + ], + [ + 81.142, + 13.174 + ], + [ + 81.291, + 12.92 + ], + [ + 81.289, + 12.856 + ], + [ + 80.885, + 13.047 + ], + [ + 80.946, + 12.665 + ], + [ + 81.19, + 12.377 + ], + [ + 80.862, + 12.395 + ], + [ + 80.754, + 12.229 + ], + [ + 80.852, + 11.832 + ], + [ + 80.523, + 11.45 + ], + [ + 80.293, + 11.467 + ], + [ + 80.631, + 11.212 + ], + [ + 80.594, + 11.016 + ], + [ + 80.389, + 10.997 + ], + [ + 80.364, + 10.778 + ], + [ + 81.008, + 10.58 + ], + [ + 81.636, + 9.998 + ], + [ + 81.745, + 9.653 + ], + [ + 81.529, + 9.509 + ], + [ + 81.54, + 9.307 + ], + [ + 81.636, + 9.416 + ], + [ + 82.316, + 9.198 + ], + [ + 82.341, + 9.417 + ], + [ + 83.019, + 9.307 + ], + [ + 82.752, + 9.054 + ], + [ + 82.399, + 9.073 + ], + [ + 83.043, + 8.873 + ], + [ + 83.211, + 8.509 + ], + [ + 83.333, + 8.908 + ], + [ + 83.575, + 8.982 + ], + [ + 83.721, + 8.799 + ], + [ + 84.084, + 9.053 + ], + [ + 84.568, + 8.655 + ], + [ + 84.908, + 8.799 + ], + [ + 85.294, + 8.619 + ], + [ + 85.876, + 8.11 + ], + [ + 85.815, + 8.527 + ], + [ + 85.67, + 8.673 + ], + [ + 85.816, + 8.78 + ], + [ + 85.741, + 9.035 + ], + [ + 86.058, + 9.508 + ], + [ + 85.343, + 10.069 + ], + [ + 85.306, + 10.487 + ], + [ + 85.041, + 10.996 + ], + [ + 85.901, + 12.285 + ], + [ + 87.961, + 12.829 + ], + [ + 88.032, + 12.43 + ], + [ + 88.178, + 12.684 + ], + [ + 89.729, + 12.646 + ], + [ + 91.933, + 12.212 + ], + [ + 92.344, + 12.031 + ], + [ + 92.78, + 11.63 + ], + [ + 92.78, + 11.486 + ], + [ + 93.046, + 11.486 + ], + [ + 93.168, + 11.266 + ], + [ + 93.459, + 11.451 + ], + [ + 95.275, + 11.666 + ], + [ + 95.517, + 12.067 + ], + [ + 95.567, + 11.595 + ], + [ + 95.737, + 11.594 + ], + [ + 95.762, + 11.741 + ], + [ + 96.026, + 11.666 + ], + [ + 96.149, + 11.92 + ], + [ + 96.292, + 11.666 + ], + [ + 96.728, + 11.884 + ], + [ + 96.838, + 11.795 + ], + [ + 96.608, + 11.521 + ], + [ + 96.85, + 11.449 + ], + [ + 97.163, + 11.666 + ], + [ + 97.238, + 11.34 + ], + [ + 97.892, + 11.087 + ], + [ + 99.539, + 11.086 + ], + [ + 100.0, + 11.521 + ], + [ + 99.733, + 10.907 + ], + [ + 99.539, + 10.939 + ], + [ + 99.224, + 10.686 + ], + [ + 98.957, + 10.76 + ], + [ + 98.39, + 10.415 + ], + [ + 98.569, + 9.889 + ], + [ + 99.078, + 9.67 + ], + [ + 97.868, + 10.068 + ], + [ + 97.625, + 10.541 + ], + [ + 97.381, + 10.579 + ], + [ + 97.323, + 9.981 + ], + [ + 97.213, + 9.852 + ], + [ + 95.711, + 9.2 + ], + [ + 94.984, + 7.275 + ], + [ + 94.307, + 7.202 + ], + [ + 93.81, + 6.712 + ], + [ + 93.737, + 4.462 + ], + [ + 93.811, + 4.135 + ], + [ + 93.665, + 3.919 + ], + [ + 93.64, + 3.338 + ], + [ + 93.411, + 2.921 + ], + [ + 93.073, + 2.738 + ], + [ + 92.756, + 2.738 + ], + [ + 92.805, + 2.919 + ], + [ + 92.501, + 2.757 + ], + [ + 93.421, + 2.03 + ], + [ + 93.714, + 1.05 + ], + [ + 93.812, + 0.0 + ] + ], + "worldSize": 100, + "meta": { + "sourceLengthKm": 1971.7732460448683, + "simplificationToleranceDeg": 0.0025, + "minCellSizeWorld": 1.5625, + "minCellSizeRealKm": 3.9936135910971187, + "boundingBox": { + "lonMin": -4.797583, + "lonMax": -1.3575, + "latMin": 47.203333, + "latMax": 48.898278 + } + } + }, + "normandie": { + "name": "normandie", + "points": [ + [ + 0.0, + 33.811 + ], + [ + 0.494, + 34.065 + ], + [ + 1.317, + 34.172 + ], + [ + 1.436, + 34.572 + ], + [ + 1.965, + 34.735 + ], + [ + 1.505, + 34.9 + ], + [ + 1.46, + 35.19 + ], + [ + 2.119, + 34.97 + ], + [ + 2.106, + 34.589 + ], + [ + 2.26, + 34.428 + ], + [ + 3.365, + 34.356 + ], + [ + 5.509, + 33.775 + ], + [ + 5.908, + 33.776 + ], + [ + 5.884, + 33.847 + ], + [ + 5.578, + 33.847 + ], + [ + 5.4, + 33.974 + ], + [ + 5.165, + 34.446 + ], + [ + 5.25, + 34.754 + ], + [ + 5.236, + 34.446 + ], + [ + 5.507, + 33.956 + ], + [ + 5.907, + 33.883 + ], + [ + 6.425, + 33.376 + ], + [ + 8.426, + 32.576 + ], + [ + 10.543, + 30.943 + ], + [ + 10.733, + 31.09 + ], + [ + 10.78, + 30.944 + ], + [ + 11.111, + 31.523 + ], + [ + 10.923, + 31.053 + ], + [ + 10.65, + 30.817 + ], + [ + 11.227, + 29.964 + ], + [ + 12.05, + 29.238 + ], + [ + 13.346, + 28.913 + ], + [ + 14.545, + 28.296 + ], + [ + 14.924, + 28.293 + ], + [ + 15.135, + 28.585 + ], + [ + 15.499, + 28.675 + ], + [ + 15.159, + 28.511 + ], + [ + 15.016, + 28.185 + ], + [ + 17.065, + 28.077 + ], + [ + 18.688, + 27.712 + ], + [ + 19.465, + 28.004 + ], + [ + 18.984, + 27.659 + ], + [ + 20.595, + 27.024 + ], + [ + 22.183, + 25.881 + ], + [ + 21.869, + 25.861 + ], + [ + 20.125, + 26.95 + ], + [ + 18.689, + 27.242 + ], + [ + 14.97, + 26.988 + ], + [ + 13.25, + 26.699 + ], + [ + 12.898, + 26.914 + ], + [ + 12.758, + 26.622 + ], + [ + 11.722, + 26.406 + ], + [ + 11.039, + 25.898 + ], + [ + 11.015, + 25.717 + ], + [ + 11.769, + 26.263 + ], + [ + 12.687, + 26.444 + ], + [ + 13.206, + 26.297 + ], + [ + 13.358, + 26.097 + ], + [ + 12.616, + 26.334 + ], + [ + 12.064, + 26.135 + ], + [ + 12.239, + 26.043 + ], + [ + 11.535, + 25.899 + ], + [ + 11.485, + 25.789 + ], + [ + 11.735, + 25.699 + ], + [ + 11.322, + 25.717 + ], + [ + 10.923, + 25.064 + ], + [ + 10.356, + 24.775 + ], + [ + 10.299, + 24.319 + ], + [ + 10.72, + 23.013 + ], + [ + 12.745, + 18.657 + ], + [ + 13.053, + 16.878 + ], + [ + 14.263, + 15.699 + ], + [ + 15.158, + 15.483 + ], + [ + 16.335, + 14.719 + ], + [ + 17.865, + 14.321 + ], + [ + 18.432, + 13.885 + ], + [ + 18.667, + 13.486 + ], + [ + 18.784, + 13.631 + ], + [ + 18.961, + 13.613 + ], + [ + 18.761, + 13.376 + ], + [ + 20.856, + 12.361 + ], + [ + 22.101, + 11.6 + ], + [ + 22.503, + 11.163 + ], + [ + 23.139, + 10.873 + ], + [ + 23.915, + 10.181 + ], + [ + 24.832, + 9.711 + ], + [ + 28.318, + 8.768 + ], + [ + 28.434, + 8.766 + ], + [ + 28.599, + 9.168 + ], + [ + 28.528, + 8.804 + ], + [ + 28.976, + 8.913 + ], + [ + 30.459, + 8.769 + ], + [ + 31.872, + 8.078 + ], + [ + 34.649, + 7.352 + ], + [ + 35.613, + 6.807 + ], + [ + 37.403, + 6.917 + ], + [ + 38.72, + 6.445 + ], + [ + 39.097, + 6.117 + ], + [ + 39.155, + 6.354 + ], + [ + 39.062, + 6.607 + ], + [ + 39.31, + 6.843 + ], + [ + 39.182, + 6.5 + ], + [ + 39.285, + 6.154 + ], + [ + 42.37, + 4.521 + ], + [ + 46.041, + 1.236 + ], + [ + 47.077, + 0.547 + ], + [ + 47.077, + 0.4 + ], + [ + 47.314, + 0.437 + ], + [ + 47.737, + 0.0 + ] + ], + "worldSize": 100, + "meta": { + "sourceLengthKm": 265.70253627275014, + "simplificationToleranceDeg": 0.0025, + "minCellSizeWorld": 1.5625, + "minCellSizeRealKm": 3.9936135910971187, + "boundingBox": { + "lonMin": -0.299167, + "lonMax": 1.390833, + "latMin": 49.267056, + "latMax": 50.075028 + } + } + } +} \ No newline at end of file diff --git a/notebooks/pyproject.toml b/notebooks/pyproject.toml index ef2dac8ab..e45d5e922 100644 --- a/notebooks/pyproject.toml +++ b/notebooks/pyproject.toml @@ -16,6 +16,8 @@ dependencies = [ "requests>=2.33.0", "beautifulsoup4", "openpyxl", + "shapely>=2.0.0", + "pyshp>=2.3.1", ] [tool.uv] diff --git a/notebooks/uv.lock b/notebooks/uv.lock index 7698b7dcd..7f312656d 100644 --- a/notebooks/uv.lock +++ b/notebooks/uv.lock @@ -705,7 +705,7 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } wheels = [ @@ -780,7 +780,7 @@ resolution-markers = [ "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } wheels = [ @@ -1209,7 +1209,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1509,17 +1509,17 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "exceptiongroup" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, - { name = "typing-extensions" }, + { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version < '3.11'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "jedi", marker = "python_full_version < '3.11'" }, + { name = "matplotlib-inline", marker = "python_full_version < '3.11'" }, + { name = "pexpect", marker = "python_full_version < '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version < '3.11'" }, + { name = "pygments", marker = "python_full_version < '3.11'" }, + { name = "stack-data", marker = "python_full_version < '3.11'" }, + { name = "traitlets", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/40/18/f8598d287006885e7136451fdea0755af4ebcbfe342836f24deefaed1164/ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624", size = 5513971, upload-time = "2026-03-27T10:02:13.94Z" } wheels = [ @@ -1539,18 +1539,18 @@ resolution-markers = [ "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "ipython-pygments-lexers" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "psutil", marker = "sys_platform != 'emscripten'" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version >= '3.11'" }, + { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" }, + { name = "jedi", marker = "python_full_version >= '3.11'" }, + { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" }, + { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" }, + { name = "psutil", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten'" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, + { name = "stack-data", marker = "python_full_version >= '3.11'" }, + { name = "traitlets", marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/e2/23/3a27530575643c8bb7bfc757a28e2e7ef80092afbf59a2bc5716320b6602/ipython-9.14.1.tar.gz", hash = "sha256:f913bf74df06d458e46ced84ca506c23797590d594b236fe60b14df213291e7b", size = 4433457, upload-time = "2026-06-05T08:12:34.921Z" } wheels = [ @@ -1562,7 +1562,7 @@ name = "ipython-pygments-lexers" version = "1.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pygments" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } wheels = [ @@ -2447,8 +2447,10 @@ dependencies = [ { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "pandas", version = "3.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "pydantic" }, + { name = "pyshp" }, { name = "python-dotenv" }, { name = "requests" }, + { name = "shapely" }, { name = "web3" }, ] @@ -2470,8 +2472,10 @@ requires-dist = [ { name = "openpyxl" }, { name = "pandas", specifier = ">=2.0.0" }, { name = "pydantic", specifier = ">=2.11.7" }, + { name = "pyshp", specifier = ">=2.3.1" }, { name = "python-dotenv", specifier = ">=1.0" }, { name = "requests", specifier = ">=2.33.0" }, + { name = "shapely", specifier = ">=2.0.0" }, { name = "web3", specifier = ">=7.12.1" }, ] @@ -2672,10 +2676,10 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "pytz", marker = "python_full_version < '3.11'" }, + { name = "tzdata", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } wheels = [ @@ -2741,9 +2745,9 @@ resolution-markers = [ "python_full_version >= '3.11' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, - { name = "python-dateutil" }, - { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/f8/87/4341c6252d1c47b08768c3d25ac487362bf403f0313ddae4a2a26c9b1b4c/pandas-3.0.3.tar.gz", hash = "sha256:696a4a00a2a2a35d4e5deb3fc946641b96c944f02230e4f76137fe35d806c4fc", size = 4651414, upload-time = "2026-05-11T18:54:29.21Z" } wheels = [ @@ -2831,7 +2835,7 @@ name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess" }, + { name = "ptyprocess", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ @@ -3329,6 +3333,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, ] +[[package]] +name = "pyshp" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/68/6e/f6f241fcb57f09c856ae78249d70cf31c576840218226ba7d88eda5ceab8/pyshp-3.1.6.tar.gz", hash = "sha256:4c4b5a4138815c93defd6ebe50bae885b7e58984f431cdb7bb5d00718ac71daf", size = 2227945, upload-time = "2026-07-25T16:53:12.324Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/72/1f79dab76eb7651b5e3622a46b18a7f93f57c3a1317b712986c58810895e/pyshp-3.1.6-py3-none-any.whl", hash = "sha256:be8cd9436dcbde477c63ea789f7dcdea3af19f17ea84f2744bfc12efe9145d1e", size = 73744, upload-time = "2026-07-25T16:53:10.889Z" }, +] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -4070,6 +4083,74 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5d/40/e1e72872c6354b306daef1703549e8e83b4d43cfea356311bf722a043752/setuptools-83.0.0-py3-none-any.whl", hash = "sha256:29b23c360f22f414dc7336bb39178cc7bcbf6021ed2733cde173f09dba19abb3", size = 1008090, upload-time = "2026-07-04T15:31:20.885Z" }, ] +[[package]] +name = "shapely" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/bc/0989043118a27cccb4e906a46b7565ce36ca7b57f5a18b78f4f1b0f72d9d/shapely-2.1.2.tar.gz", hash = "sha256:2ed4ecb28320a433db18a5bf029986aa8afcfd740745e78847e330d5d94922a9", size = 315489, upload-time = "2025-09-24T13:51:41.432Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/05/89/c3548aa9b9812a5d143986764dededfa48d817714e947398bdda87c77a72/shapely-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7ae48c236c0324b4e139bea88a306a04ca630f49be66741b340729d380d8f52f", size = 1825959, upload-time = "2025-09-24T13:50:00.682Z" }, + { url = "https://files.pythonhosted.org/packages/ce/8a/7ebc947080442edd614ceebe0ce2cdbd00c25e832c240e1d1de61d0e6b38/shapely-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eba6710407f1daa8e7602c347dfc94adc02205ec27ed956346190d66579eb9ea", size = 1629196, upload-time = "2025-09-24T13:50:03.447Z" }, + { url = "https://files.pythonhosted.org/packages/c8/86/c9c27881c20d00fc409e7e059de569d5ed0abfcec9c49548b124ebddea51/shapely-2.1.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ef4a456cc8b7b3d50ccec29642aa4aeda959e9da2fe9540a92754770d5f0cf1f", size = 2951065, upload-time = "2025-09-24T13:50:05.266Z" }, + { url = "https://files.pythonhosted.org/packages/50/8a/0ab1f7433a2a85d9e9aea5b1fbb333f3b09b309e7817309250b4b7b2cc7a/shapely-2.1.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e38a190442aacc67ff9f75ce60aec04893041f16f97d242209106d502486a142", size = 3058666, upload-time = "2025-09-24T13:50:06.872Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c6/5a30ffac9c4f3ffd5b7113a7f5299ccec4713acd5ee44039778a7698224e/shapely-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:40d784101f5d06a1fd30b55fc11ea58a61be23f930d934d86f19a180909908a4", size = 3966905, upload-time = "2025-09-24T13:50:09.417Z" }, + { url = "https://files.pythonhosted.org/packages/9c/72/e92f3035ba43e53959007f928315a68fbcf2eeb4e5ededb6f0dc7ff1ecc3/shapely-2.1.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f6f6cd5819c50d9bcf921882784586aab34a4bd53e7553e175dece6db513a6f0", size = 4129260, upload-time = "2025-09-24T13:50:11.183Z" }, + { url = "https://files.pythonhosted.org/packages/42/24/605901b73a3d9f65fa958e63c9211f4be23d584da8a1a7487382fac7fdc5/shapely-2.1.2-cp310-cp310-win32.whl", hash = "sha256:fe9627c39c59e553c90f5bc3128252cb85dc3b3be8189710666d2f8bc3a5503e", size = 1544301, upload-time = "2025-09-24T13:50:12.521Z" }, + { url = "https://files.pythonhosted.org/packages/e1/89/6db795b8dd3919851856bd2ddd13ce434a748072f6fdee42ff30cbd3afa3/shapely-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:1d0bfb4b8f661b3b4ec3565fa36c340bfb1cda82087199711f86a88647d26b2f", size = 1722074, upload-time = "2025-09-24T13:50:13.909Z" }, + { url = "https://files.pythonhosted.org/packages/8f/8d/1ff672dea9ec6a7b5d422eb6d095ed886e2e523733329f75fdcb14ee1149/shapely-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:91121757b0a36c9aac3427a651a7e6567110a4a67c97edf04f8d55d4765f6618", size = 1820038, upload-time = "2025-09-24T13:50:15.628Z" }, + { url = "https://files.pythonhosted.org/packages/4f/ce/28fab8c772ce5db23a0d86bf0adaee0c4c79d5ad1db766055fa3dab442e2/shapely-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:16a9c722ba774cf50b5d4541242b4cce05aafd44a015290c82ba8a16931ff63d", size = 1626039, upload-time = "2025-09-24T13:50:16.881Z" }, + { url = "https://files.pythonhosted.org/packages/70/8b/868b7e3f4982f5006e9395c1e12343c66a8155c0374fdc07c0e6a1ab547d/shapely-2.1.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cc4f7397459b12c0b196c9efe1f9d7e92463cbba142632b4cc6d8bbbbd3e2b09", size = 3001519, upload-time = "2025-09-24T13:50:18.606Z" }, + { url = "https://files.pythonhosted.org/packages/13/02/58b0b8d9c17c93ab6340edd8b7308c0c5a5b81f94ce65705819b7416dba5/shapely-2.1.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:136ab87b17e733e22f0961504d05e77e7be8c9b5a8184f685b4a91a84efe3c26", size = 3110842, upload-time = "2025-09-24T13:50:21.77Z" }, + { url = "https://files.pythonhosted.org/packages/af/61/8e389c97994d5f331dcffb25e2fa761aeedfb52b3ad9bcdd7b8671f4810a/shapely-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:16c5d0fc45d3aa0a69074979f4f1928ca2734fb2e0dde8af9611e134e46774e7", size = 4021316, upload-time = "2025-09-24T13:50:23.626Z" }, + { url = "https://files.pythonhosted.org/packages/d3/d4/9b2a9fe6039f9e42ccf2cb3e84f219fd8364b0c3b8e7bbc857b5fbe9c14c/shapely-2.1.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ddc759f72b5b2b0f54a7e7cde44acef680a55019eb52ac63a7af2cf17cb9cd2", size = 4178586, upload-time = "2025-09-24T13:50:25.443Z" }, + { url = "https://files.pythonhosted.org/packages/16/f6/9840f6963ed4decf76b08fd6d7fed14f8779fb7a62cb45c5617fa8ac6eab/shapely-2.1.2-cp311-cp311-win32.whl", hash = "sha256:2fa78b49485391224755a856ed3b3bd91c8455f6121fee0db0e71cefb07d0ef6", size = 1543961, upload-time = "2025-09-24T13:50:26.968Z" }, + { url = "https://files.pythonhosted.org/packages/38/1e/3f8ea46353c2a33c1669eb7327f9665103aa3a8dfe7f2e4ef714c210b2c2/shapely-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:c64d5c97b2f47e3cd9b712eaced3b061f2b71234b3fc263e0fcf7d889c6559dc", size = 1722856, upload-time = "2025-09-24T13:50:28.497Z" }, + { url = "https://files.pythonhosted.org/packages/24/c0/f3b6453cf2dfa99adc0ba6675f9aaff9e526d2224cbd7ff9c1a879238693/shapely-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fe2533caae6a91a543dec62e8360fe86ffcdc42a7c55f9dfd0128a977a896b94", size = 1833550, upload-time = "2025-09-24T13:50:30.019Z" }, + { url = "https://files.pythonhosted.org/packages/86/07/59dee0bc4b913b7ab59ab1086225baca5b8f19865e6101db9ebb7243e132/shapely-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ba4d1333cc0bc94381d6d4308d2e4e008e0bd128bdcff5573199742ee3634359", size = 1643556, upload-time = "2025-09-24T13:50:32.291Z" }, + { url = "https://files.pythonhosted.org/packages/26/29/a5397e75b435b9895cd53e165083faed5d12fd9626eadec15a83a2411f0f/shapely-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0bd308103340030feef6c111d3eb98d50dc13feea33affc8a6f9fa549e9458a3", size = 2988308, upload-time = "2025-09-24T13:50:33.862Z" }, + { url = "https://files.pythonhosted.org/packages/b9/37/e781683abac55dde9771e086b790e554811a71ed0b2b8a1e789b7430dd44/shapely-2.1.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1e7d4d7ad262a48bb44277ca12c7c78cb1b0f56b32c10734ec9a1d30c0b0c54b", size = 3099844, upload-time = "2025-09-24T13:50:35.459Z" }, + { url = "https://files.pythonhosted.org/packages/d8/f3/9876b64d4a5a321b9dc482c92bb6f061f2fa42131cba643c699f39317cb9/shapely-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e9eddfe513096a71896441a7c37db72da0687b34752c4e193577a145c71736fc", size = 3988842, upload-time = "2025-09-24T13:50:37.478Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a0/704c7292f7014c7e74ec84eddb7b109e1fbae74a16deae9c1504b1d15565/shapely-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:980c777c612514c0cf99bc8a9de6d286f5e186dcaf9091252fcd444e5638193d", size = 4152714, upload-time = "2025-09-24T13:50:39.9Z" }, + { url = "https://files.pythonhosted.org/packages/53/46/319c9dc788884ad0785242543cdffac0e6530e4d0deb6c4862bc4143dcf3/shapely-2.1.2-cp312-cp312-win32.whl", hash = "sha256:9111274b88e4d7b54a95218e243282709b330ef52b7b86bc6aaf4f805306f454", size = 1542745, upload-time = "2025-09-24T13:50:41.414Z" }, + { url = "https://files.pythonhosted.org/packages/ec/bf/cb6c1c505cb31e818e900b9312d514f381fbfa5c4363edfce0fcc4f8c1a4/shapely-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:743044b4cfb34f9a67205cee9279feaf60ba7d02e69febc2afc609047cb49179", size = 1722861, upload-time = "2025-09-24T13:50:43.35Z" }, + { url = "https://files.pythonhosted.org/packages/c3/90/98ef257c23c46425dc4d1d31005ad7c8d649fe423a38b917db02c30f1f5a/shapely-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b510dda1a3672d6879beb319bc7c5fd302c6c354584690973c838f46ec3e0fa8", size = 1832644, upload-time = "2025-09-24T13:50:44.886Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ab/0bee5a830d209adcd3a01f2d4b70e587cdd9fd7380d5198c064091005af8/shapely-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8cff473e81017594d20ec55d86b54bc635544897e13a7cfc12e36909c5309a2a", size = 1642887, upload-time = "2025-09-24T13:50:46.735Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5e/7d7f54ba960c13302584c73704d8c4d15404a51024631adb60b126a4ae88/shapely-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe7b77dc63d707c09726b7908f575fc04ff1d1ad0f3fb92aec212396bc6cfe5e", size = 2970931, upload-time = "2025-09-24T13:50:48.374Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a2/83fc37e2a58090e3d2ff79175a95493c664bcd0b653dd75cb9134645a4e5/shapely-2.1.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7ed1a5bbfb386ee8332713bf7508bc24e32d24b74fc9a7b9f8529a55db9f4ee6", size = 3082855, upload-time = "2025-09-24T13:50:50.037Z" }, + { url = "https://files.pythonhosted.org/packages/44/2b/578faf235a5b09f16b5f02833c53822294d7f21b242f8e2d0cf03fb64321/shapely-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a84e0582858d841d54355246ddfcbd1fce3179f185da7470f41ce39d001ee1af", size = 3979960, upload-time = "2025-09-24T13:50:51.74Z" }, + { url = "https://files.pythonhosted.org/packages/4d/04/167f096386120f692cc4ca02f75a17b961858997a95e67a3cb6a7bbd6b53/shapely-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc3487447a43d42adcdf52d7ac73804f2312cbfa5d433a7d2c506dcab0033dfd", size = 4142851, upload-time = "2025-09-24T13:50:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/48/74/fb402c5a6235d1c65a97348b48cdedb75fb19eca2b1d66d04969fc1c6091/shapely-2.1.2-cp313-cp313-win32.whl", hash = "sha256:9c3a3c648aedc9f99c09263b39f2d8252f199cb3ac154fadc173283d7d111350", size = 1541890, upload-time = "2025-09-24T13:50:55.337Z" }, + { url = "https://files.pythonhosted.org/packages/41/47/3647fe7ad990af60ad98b889657a976042c9988c2807cf322a9d6685f462/shapely-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:ca2591bff6645c216695bdf1614fca9c82ea1144d4a7591a466fef64f28f0715", size = 1722151, upload-time = "2025-09-24T13:50:57.153Z" }, + { url = "https://files.pythonhosted.org/packages/3c/49/63953754faa51ffe7d8189bfbe9ca34def29f8c0e34c67cbe2a2795f269d/shapely-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2d93d23bdd2ed9dc157b46bc2f19b7da143ca8714464249bef6771c679d5ff40", size = 1834130, upload-time = "2025-09-24T13:50:58.49Z" }, + { url = "https://files.pythonhosted.org/packages/7f/ee/dce001c1984052970ff60eb4727164892fb2d08052c575042a47f5a9e88f/shapely-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01d0d304b25634d60bd7cf291828119ab55a3bab87dc4af1e44b07fb225f188b", size = 1642802, upload-time = "2025-09-24T13:50:59.871Z" }, + { url = "https://files.pythonhosted.org/packages/da/e7/fc4e9a19929522877fa602f705706b96e78376afb7fad09cad5b9af1553c/shapely-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8d8382dd120d64b03698b7298b89611a6ea6f55ada9d39942838b79c9bc89801", size = 3018460, upload-time = "2025-09-24T13:51:02.08Z" }, + { url = "https://files.pythonhosted.org/packages/a1/18/7519a25db21847b525696883ddc8e6a0ecaa36159ea88e0fef11466384d0/shapely-2.1.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:19efa3611eef966e776183e338b2d7ea43569ae99ab34f8d17c2c054d3205cc0", size = 3095223, upload-time = "2025-09-24T13:51:04.472Z" }, + { url = "https://files.pythonhosted.org/packages/48/de/b59a620b1f3a129c3fecc2737104a0a7e04e79335bd3b0a1f1609744cf17/shapely-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:346ec0c1a0fcd32f57f00e4134d1200e14bf3f5ae12af87ba83ca275c502498c", size = 4030760, upload-time = "2025-09-24T13:51:06.455Z" }, + { url = "https://files.pythonhosted.org/packages/96/b3/c6655ee7232b417562bae192ae0d3ceaadb1cc0ffc2088a2ddf415456cc2/shapely-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6305993a35989391bd3476ee538a5c9a845861462327efe00dd11a5c8c709a99", size = 4170078, upload-time = "2025-09-24T13:51:08.584Z" }, + { url = "https://files.pythonhosted.org/packages/a0/8e/605c76808d73503c9333af8f6cbe7e1354d2d238bda5f88eea36bfe0f42a/shapely-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:c8876673449f3401f278c86eb33224c5764582f72b653a415d0e6672fde887bf", size = 1559178, upload-time = "2025-09-24T13:51:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/36/f7/d317eb232352a1f1444d11002d477e54514a4a6045536d49d0c59783c0da/shapely-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:4a44bc62a10d84c11a7a3d7c1c4fe857f7477c3506e24c9062da0db0ae0c449c", size = 1739756, upload-time = "2025-09-24T13:51:12.105Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c4/3ce4c2d9b6aabd27d26ec988f08cb877ba9e6e96086eff81bfea93e688c7/shapely-2.1.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:9a522f460d28e2bf4e12396240a5fc1518788b2fcd73535166d748399ef0c223", size = 1831290, upload-time = "2025-09-24T13:51:13.56Z" }, + { url = "https://files.pythonhosted.org/packages/17/b9/f6ab8918fc15429f79cb04afa9f9913546212d7fb5e5196132a2af46676b/shapely-2.1.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1ff629e00818033b8d71139565527ced7d776c269a49bd78c9df84e8f852190c", size = 1641463, upload-time = "2025-09-24T13:51:14.972Z" }, + { url = "https://files.pythonhosted.org/packages/a5/57/91d59ae525ca641e7ac5551c04c9503aee6f29b92b392f31790fcb1a4358/shapely-2.1.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f67b34271dedc3c653eba4e3d7111aa421d5be9b4c4c7d38d30907f796cb30df", size = 2970145, upload-time = "2025-09-24T13:51:16.961Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cb/4948be52ee1da6927831ab59e10d4c29baa2a714f599f1f0d1bc747f5777/shapely-2.1.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21952dc00df38a2c28375659b07a3979d22641aeb104751e769c3ee825aadecf", size = 3073806, upload-time = "2025-09-24T13:51:18.712Z" }, + { url = "https://files.pythonhosted.org/packages/03/83/f768a54af775eb41ef2e7bec8a0a0dbe7d2431c3e78c0a8bdba7ab17e446/shapely-2.1.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1f2f33f486777456586948e333a56ae21f35ae273be99255a191f5c1fa302eb4", size = 3980803, upload-time = "2025-09-24T13:51:20.37Z" }, + { url = "https://files.pythonhosted.org/packages/9f/cb/559c7c195807c91c79d38a1f6901384a2878a76fbdf3f1048893a9b7534d/shapely-2.1.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cf831a13e0d5a7eb519e96f58ec26e049b1fad411fc6fc23b162a7ce04d9cffc", size = 4133301, upload-time = "2025-09-24T13:51:21.887Z" }, + { url = "https://files.pythonhosted.org/packages/80/cd/60d5ae203241c53ef3abd2ef27c6800e21afd6c94e39db5315ea0cbafb4a/shapely-2.1.2-cp314-cp314-win32.whl", hash = "sha256:61edcd8d0d17dd99075d320a1dd39c0cb9616f7572f10ef91b4b5b00c4aeb566", size = 1583247, upload-time = "2025-09-24T13:51:23.401Z" }, + { url = "https://files.pythonhosted.org/packages/74/d4/135684f342e909330e50d31d441ace06bf83c7dc0777e11043f99167b123/shapely-2.1.2-cp314-cp314-win_amd64.whl", hash = "sha256:a444e7afccdb0999e203b976adb37ea633725333e5b119ad40b1ca291ecf311c", size = 1773019, upload-time = "2025-09-24T13:51:24.873Z" }, + { url = "https://files.pythonhosted.org/packages/a3/05/a44f3f9f695fa3ada22786dc9da33c933da1cbc4bfe876fe3a100bafe263/shapely-2.1.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5ebe3f84c6112ad3d4632b1fd2290665aa75d4cef5f6c5d77c4c95b324527c6a", size = 1834137, upload-time = "2025-09-24T13:51:26.665Z" }, + { url = "https://files.pythonhosted.org/packages/52/7e/4d57db45bf314573427b0a70dfca15d912d108e6023f623947fa69f39b72/shapely-2.1.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5860eb9f00a1d49ebb14e881f5caf6c2cf472c7fd38bd7f253bbd34f934eb076", size = 1642884, upload-time = "2025-09-24T13:51:28.029Z" }, + { url = "https://files.pythonhosted.org/packages/5a/27/4e29c0a55d6d14ad7422bf86995d7ff3f54af0eba59617eb95caf84b9680/shapely-2.1.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b705c99c76695702656327b819c9660768ec33f5ce01fa32b2af62b56ba400a1", size = 3018320, upload-time = "2025-09-24T13:51:29.903Z" }, + { url = "https://files.pythonhosted.org/packages/9f/bb/992e6a3c463f4d29d4cd6ab8963b75b1b1040199edbd72beada4af46bde5/shapely-2.1.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a1fd0ea855b2cf7c9cddaf25543e914dd75af9de08785f20ca3085f2c9ca60b0", size = 3094931, upload-time = "2025-09-24T13:51:32.699Z" }, + { url = "https://files.pythonhosted.org/packages/9c/16/82e65e21070e473f0ed6451224ed9fa0be85033d17e0c6e7213a12f59d12/shapely-2.1.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:df90e2db118c3671a0754f38e36802db75fe0920d211a27481daf50a711fdf26", size = 4030406, upload-time = "2025-09-24T13:51:34.189Z" }, + { url = "https://files.pythonhosted.org/packages/7c/75/c24ed871c576d7e2b64b04b1fe3d075157f6eb54e59670d3f5ffb36e25c7/shapely-2.1.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:361b6d45030b4ac64ddd0a26046906c8202eb60d0f9f53085f5179f1d23021a0", size = 4169511, upload-time = "2025-09-24T13:51:36.297Z" }, + { url = "https://files.pythonhosted.org/packages/b1/f7/b3d1d6d18ebf55236eec1c681ce5e665742aab3c0b7b232720a7d43df7b6/shapely-2.1.2-cp314-cp314t-win32.whl", hash = "sha256:b54df60f1fbdecc8ebc2c5b11870461a6417b3d617f555e5033f1505d36e5735", size = 1602607, upload-time = "2025-09-24T13:51:37.757Z" }, + { url = "https://files.pythonhosted.org/packages/9a/f6/f09272a71976dfc138129b8faf435d064a811ae2f708cb147dccdf7aacdb/shapely-2.1.2-cp314-cp314t-win_amd64.whl", hash = "sha256:0036ac886e0923417932c2e6369b6c52e38e0ff5d9120b90eef5cd9a5fc5cae9", size = 1796682, upload-time = "2025-09-24T13:51:39.233Z" }, +] + [[package]] name = "six" version = "1.17.0" diff --git a/website/blog/kuesten_dimension.mdx b/website/blog/kuesten_dimension.mdx index 0c4728c5d..7929fde39 100644 --- a/website/blog/kuesten_dimension.mdx +++ b/website/blog/kuesten_dimension.mdx @@ -6,7 +6,8 @@ description: "Eine Küste wird länger, je genauer man sie misst. Mit einem einf --- import Foldable from "../components/Foldable"; -import WidgetPlaceholder from "../components/blog/box-dimension/WidgetPlaceholder"; +import KaestchenSpiel from "../components/blog/box-dimension/KaestchenSpiel"; +import KuestenSpiel from "../components/blog/box-dimension/KuestenSpiel"; Stell dir vor, du schaust dir eine Bucht in der Bretagne auf einer Karte an. Sie sieht klein aus, vielleicht so groß wie dein Daumennagel. Jetzt stell dir vor, du müsstest sie zu Fuß umrunden, immer dicht am Wasser entlang. Um jeden Felsen herum, in jede kleine Nebenbucht hinein, um jeden Stein, der ins Wasser ragt. @@ -22,10 +23,7 @@ Bevor du weiterliest: Was schätzt du — bei einer **geraden Linie**, wie viel Probier es im Widget unten aus, an drei Formen: einer geraden Linie, einem Kreis und einem voll ausgemalten Quadrat. - + Das Ergebnis: Bei der Linie und beim Kreis brauchst du **doppelt** so viele Kästchen, egal wie oft du halbierst. Beim Quadrat brauchst du **viermal** so viele. @@ -37,29 +35,23 @@ Wenn du beide Zahlen nebeneinanderlegst, siehst du: Verdoppelt sich der Faktor m Jetzt kommt der Clou. Was passiert, wenn du dasselbe Spiel nicht mit einer Linie, sondern mit einer echten Küste spielst? - + Schau dir den Faktor an, den das Widget für die Bretagne anzeigt. Er ist **nicht 2** und **nicht 4**. Er liegt irgendwo dazwischen. -Das bedeutet: Die Küste ist mehr als eine Linie, aber weniger als eine volle Fläche. Ihre Dimension liegt zwischen 1 und 2 — zum Beispiel bei 1,3. Eine Zahl, die keine ganze Zahl ist, obwohl "Dimension" für dich bisher wahrscheinlich immer 1, 2 oder 3 war. +Das bedeutet: Die Küste ist mehr als eine Linie, aber weniger als eine volle Fläche. Ihre Dimension liegt zwischen 1 und 2 — das Widget zeigt für die Bretagne ungefähr 1,3. Eine Zahl, die keine ganze Zahl ist, obwohl "Dimension" für dich bisher wahrscheinlich immer 1, 2 oder 3 war. Formen, deren Dimension zwischen den ganzen Zahlen liegt, nennt man **Fraktale**. Eine Küste ist ein Fraktal, weil sie in jedem Maßstab neue kleine Zacken zeigt — zoomst du rein, taucht die nächste Miniaturbucht auf, und die nächste, und die nächste. -Schalte jetzt im Widget auf die Küste der Normandie um. Der Faktor sinkt deutlich. Diese Küste sieht auf der Karte ganz anders aus als die Bretagne: viel glatter, viel gerader, mit weniger kleinen Einbuchtungen. Und genau das zeigt sich auch im gemessenen Faktor — eine glattere Küste liegt näher an der Dimension 1, eine zerklüftete Küste näher an der Dimension 2. +Schalte jetzt im Widget auf die Küste der Normandie um. Der Faktor sinkt deutlich, auf eine Dimension von ungefähr 1,1. Diese Küste sieht auf der Karte ganz anders aus als die Bretagne: viel glatter, viel gerader, mit weniger kleinen Einbuchtungen. Und genau das zeigt sich auch im gemessenen Faktor — eine glattere Küste liegt näher an der Dimension 1, eine zerklüftete Küste näher an der Dimension 2. Der Mathematiker Benoît Mandelbrot hat genau das 1967 zum ersten Mal systematisch gemessen, an der Westküste Großbritanniens. Er kam auf eine Dimension von etwa 1,25 — eine Zahl, die bis heute in jedem Buch über Fraktale steht. ## Selber malen -Der beste Weg, ein Gefühl dafür zu bekommen, ist: selbst ausprobieren. +Der beste Weg, ein Gefühl dafür zu bekommen, ist: selbst ausprobieren. Tipp im Widget oben auf "✏️ Selber malen" — oder hier, in einer frischen Runde: - + Mal mit dem Finger eine ruhige, geschwungene Linie. Schau dir ihre Dimension an — sie sollte nah bei 1 liegen. Mal jetzt eine wilde, zackige Kritzellinie, so wild du kannst. Die Dimension steigt. @@ -79,10 +71,6 @@ Dabei ist $\epsilon$ die Kantenlänge eines Kästchens und $N(\epsilon)$ die Anz Mandelbrots Originalarbeit: B. Mandelbrot, *How Long Is the Coast of Britain? Statistical Self-Similarity and Fractional Dimension*, Science 156 (1967). Er maß D ≈ 1,25 für die Westküste Großbritanniens und D ≈ 1,02 für die vergleichsweise glatte Küste Südafrikas. -Die hier verwendeten Küstenumrisse stammen aus der GSHHG-Datenbank (Wessel & Smith), public domain. +Die hier verwendeten Küstenumrisse stammen aus der GSHHG-Datenbank (Wessel & Smith), public domain. Gemessen mit demselben Kästchen-Trick wie oben, über vier Halbierungsschritte: Bretagne ≈ 1,34, Normandie ≈ 1,14.
- ---- - -*[TODO: Fred — konkrete Werte für Bretagne und Normandie einsetzen, sobald das Widget echte Daten misst (Implementierungsschritt 7). Bis dahin bleibt der Text bewusst ohne feste Zahl für die Bretagne selbst, außer dem Mandelbrot-Referenzwert für Großbritannien.]* diff --git a/website/components/blog/box-dimension/BoxCanvas.tsx b/website/components/blog/box-dimension/BoxCanvas.tsx new file mode 100644 index 000000000..46b3b83f5 --- /dev/null +++ b/website/components/blog/box-dimension/BoxCanvas.tsx @@ -0,0 +1,173 @@ +import React, { useEffect, useRef, useState } from "react"; +import { css } from "../../../styled-system/css"; +import { ESSAY_ACCENT } from "../palette"; +import { countLineCells, countFilledCells, parseCellKey, type Point } from "./boxCounting"; + +const GRID_COLOR = "#d1d5db"; // gray-300, chrome not brand — literal per palette.ts convention +const HIGHLIGHT_FILL = "rgba(124, 58, 237, 0.22)"; // translucent essay-accent-ish purple +const FILL_ALPHA = 0.25; + +const wrapper = css({ + position: "relative", + width: "100%", + aspectRatio: "1 / 1", + maxWidth: "360px", + mx: "auto", +}); + +const canvasStyle = css({ + display: "block", + width: "100%", + height: "100%", + border: "1px solid token(colors.border, #e5e7eb)", + borderRadius: "md", + bg: "white", +}); + +const drawableCanvasStyle = css({ + touchAction: "none", + cursor: "crosshair", +}); + +export interface BoxCanvasProps { + mode: "line" | "filled"; + points: Point[]; + worldSize: number; + cellSize: number; + closed?: boolean; + highlightHits?: boolean; + showGrid?: boolean; + drawEnabled?: boolean; + /** BoxCanvas converts client coordinates to world coordinates before calling this. */ + onPointerDraw?: (p: Point) => void; + className?: string; +} + +export function BoxCanvas({ + mode, + points, + worldSize, + cellSize, + closed = false, + highlightHits = true, + showGrid = true, + drawEnabled = false, + onPointerDraw, + className, +}: BoxCanvasProps) { + const wrapperRef = useRef(null); + const canvasRef = useRef(null); + const [cssWidth, setCssWidth] = useState(300); + + useEffect(() => { + const wrapperEl = wrapperRef.current; + if (!wrapperEl) return; + + const observer = new ResizeObserver((entries) => { + const width = entries[0]?.contentRect.width; + if (width && width > 0) setCssWidth(width); + }); + observer.observe(wrapperEl); + return () => observer.disconnect(); + }, []); + + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + const ctx = canvas.getContext("2d"); + if (!ctx) return; // e.g. jsdom in tests — draw calls are a no-op + + const dpr = window.devicePixelRatio || 1; + canvas.width = cssWidth * dpr; + canvas.height = cssWidth * dpr; + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + ctx.clearRect(0, 0, cssWidth, cssWidth); + + const scale = cssWidth / worldSize; + + if (showGrid && cellSize > 0) { + ctx.strokeStyle = GRID_COLOR; + ctx.lineWidth = 1; + for (let x = 0; x <= worldSize + 1e-9; x += cellSize) { + const px = Math.round(x * scale) + 0.5; + ctx.beginPath(); + ctx.moveTo(px, 0); + ctx.lineTo(px, cssWidth); + ctx.stroke(); + } + for (let y = 0; y <= worldSize + 1e-9; y += cellSize) { + const py = Math.round(y * scale) + 0.5; + ctx.beginPath(); + ctx.moveTo(0, py); + ctx.lineTo(cssWidth, py); + ctx.stroke(); + } + } + + if (highlightHits && cellSize > 0 && points.length > 0) { + const result = mode === "filled" ? countFilledCells(points, cellSize) : countLineCells(points, cellSize, closed); + ctx.fillStyle = HIGHLIGHT_FILL; + for (const key of result.cells) { + const [col, row] = parseCellKey(key); + ctx.fillRect(col * cellSize * scale, row * cellSize * scale, cellSize * scale, cellSize * scale); + } + } + + if (points.length > 0) { + ctx.beginPath(); + ctx.moveTo(points[0].x * scale, points[0].y * scale); + for (let i = 1; i < points.length; i++) { + ctx.lineTo(points[i].x * scale, points[i].y * scale); + } + if (closed || mode === "filled") ctx.closePath(); + + if (mode === "filled") { + ctx.globalAlpha = FILL_ALPHA; + ctx.fillStyle = ESSAY_ACCENT; + ctx.fill(); + ctx.globalAlpha = 1; + } + + ctx.strokeStyle = ESSAY_ACCENT; + ctx.lineWidth = 2; + ctx.stroke(); + } + }, [points, cellSize, mode, closed, highlightHits, showGrid, cssWidth, worldSize]); + + function toWorldPoint(clientX: number, clientY: number): Point | null { + const canvas = canvasRef.current; + if (!canvas) return null; + const rect = canvas.getBoundingClientRect(); + const scale = rect.width / worldSize; + return { x: (clientX - rect.left) / scale, y: (clientY - rect.top) / scale }; + } + + function handlePointerDown(e: React.PointerEvent) { + if (!drawEnabled || !onPointerDraw) return; + e.preventDefault(); + e.currentTarget.setPointerCapture(e.pointerId); + const p = toWorldPoint(e.clientX, e.clientY); + if (p) onPointerDraw(p); + } + + function handlePointerMove(e: React.PointerEvent) { + if (!drawEnabled || !onPointerDraw) return; + if (e.buttons === 0 && e.pointerType !== "touch") return; + e.preventDefault(); + const p = toWorldPoint(e.clientX, e.clientY); + if (p) onPointerDraw(p); + } + + return ( +
+ +
+ ); +} + +export default BoxCanvas; diff --git a/website/components/blog/box-dimension/DimensionSkala.tsx b/website/components/blog/box-dimension/DimensionSkala.tsx new file mode 100644 index 000000000..3f85b24fd --- /dev/null +++ b/website/components/blog/box-dimension/DimensionSkala.tsx @@ -0,0 +1,91 @@ +import React from "react"; +import { css } from "../../../styled-system/css"; +import { ESSAY_ACCENT } from "../palette"; + +/** + * Maps a halving-factor (how many times more cells you need when you halve the + * cell size — 2x for a curve, 4x for a filled area) onto a dimension between 1 + * and 2. This is exactly log2(factor) + 1, but that never appears in the UI — + * the widgets only ever show the gauge position, never the exponent. + */ +export function factorToDimension(factor: number): number { + const clamped = Math.max(2, Math.min(factor, 4)); + return Math.log2(clamped); +} + +export interface HalvingSample { + cellSize: number; + count: number; +} + +/** + * Combines two or more halving steps into one stabilized factor, via the + * geometric mean per step between the coarsest and finest sample. Ratios + * telescope, so this is the same thing as an average log-log slope — computed + * without ever naming a logarithm. + */ +export function aggregateFactor(samples: HalvingSample[]): number | null { + if (samples.length < 2) return null; + + const sorted = [...samples].sort((a, b) => b.cellSize - a.cellSize); + const coarsest = sorted[0]; + const finest = sorted[sorted.length - 1]; + + if (coarsest.count <= 0 || finest.count <= 0) return null; + + const numHalvings = Math.log2(coarsest.cellSize / finest.cellSize); + if (!isFinite(numHalvings) || numHalvings <= 0) return null; + + return Math.pow(finest.count / coarsest.count, 1 / numHalvings); +} + +const wrapper = css({ mt: "3" }); + +const track = css({ + position: "relative", + height: "8px", + bg: "gray.200", + borderRadius: "full", +}); + +const marker = css({ + position: "absolute", + top: "-4px", + width: "4px", + height: "16px", + borderRadius: "sm", +}); + +const labels = css({ + display: "flex", + justifyContent: "space-between", + fontSize: "xs", + color: "gray.600", + mt: "1", +}); + +export interface DimensionSkalaProps { + /** Measured halving-factor, or null before enough samples exist. */ + factor: number | null; +} + +export function DimensionSkala({ factor }: DimensionSkalaProps) { + const dimension = factor !== null ? factorToDimension(factor) : null; + const percent = dimension !== null ? Math.max(0, Math.min(1, dimension - 1)) * 100 : null; + + return ( +
+
+ {percent !== null && ( +
+ )} +
+
+ Linie (1) + Fläche (2) +
+
+ ); +} + +export default DimensionSkala; diff --git a/website/components/blog/box-dimension/KaestchenSpiel.tsx b/website/components/blog/box-dimension/KaestchenSpiel.tsx new file mode 100644 index 000000000..a172c6066 --- /dev/null +++ b/website/components/blog/box-dimension/KaestchenSpiel.tsx @@ -0,0 +1,178 @@ +import React, { useState } from "react"; +import { css } from "../../../styled-system/css"; +import { BoxCanvas } from "./BoxCanvas"; +import { DimensionSkala, aggregateFactor, type HalvingSample } from "./DimensionSkala"; +import { Messreihe } from "./Messreihe"; +import { countLineCells, countFilledCells, type Point } from "./boxCounting"; +import { DEMO_SHAPES, WORLD_SIZE, type ShapeDefinition } from "./shapes"; + +const CELL_SIZE_STEPS = [25, 12.5, 6.25, 3.125, 1.5625]; +const MIN_DRAW_POINT_DISTANCE = 1.5; // world units — throttles pointermove sampling + +type ShapeId = ShapeDefinition["id"] | "draw"; + +const wrapper = css({ my: "6", p: "4", border: "1px solid token(colors.border, #e5e7eb)", borderRadius: "md" }); + +const shapeRow = css({ display: "flex", flexWrap: "wrap", gap: "2", mb: "3" }); + +function shapeButtonStyle(active: boolean) { + return css({ + padding: "6px 12px", + borderRadius: "sm", + border: "1px solid token(colors.border, #e5e7eb)", + backgroundColor: active ? "explore" : "white", + color: active ? "white" : "gray.700", + fontSize: "sm", + fontWeight: active ? "bold" : "normal", + cursor: "pointer", + }); +} + +function actionButtonStyle(disabled: boolean) { + return css({ + padding: "8px 16px", + borderRadius: "sm", + border: "none", + backgroundColor: disabled ? "gray.300" : "explore", + color: "white", + fontSize: "sm", + fontWeight: "bold", + cursor: disabled ? "not-allowed" : "pointer", + }); +} + +const controlsRow = css({ display: "flex", gap: "2", alignItems: "center", flexWrap: "wrap", mt: "3" }); + +const bigNumber = css({ fontSize: "2xl", fontWeight: "bold", color: "explore", mt: "3" }); + +const hint = css({ fontSize: "sm", color: "gray.600", fontStyle: "italic", mt: "2" }); + +export interface KaestchenSpielProps { + /** "shapes" (default): Linie/Kreis/Quadrat, kein Mal-Button. "draw": nur der Mal-Canvas. */ + variant?: "shapes" | "draw"; +} + +export function KaestchenSpiel({ variant = "shapes" }: KaestchenSpielProps) { + const [shapeId, setShapeId] = useState(variant === "draw" ? "draw" : "line"); + const [stepIndex, setStepIndex] = useState(0); + const [revealed, setRevealed] = useState(false); + const [samples, setSamples] = useState([]); + const [drawnPoints, setDrawnPoints] = useState([]); + + const activeShape = shapeId === "draw" ? null : DEMO_SHAPES.find((s) => s.id === shapeId)!; + const points = shapeId === "draw" ? drawnPoints : activeShape!.points; + const mode = shapeId === "draw" ? "line" : activeShape!.mode; + const closed = shapeId === "draw" ? false : activeShape!.closed; + const cellSize = CELL_SIZE_STEPS[stepIndex]; + + function resetProgress() { + setStepIndex(0); + setRevealed(false); + setSamples([]); + } + + function handleSelectShape(id: ShapeId) { + setShapeId(id); + resetProgress(); + } + + function handleClearDrawing() { + setDrawnPoints([]); + resetProgress(); + } + + function handlePointerDraw(p: Point) { + setDrawnPoints((prev) => { + if (prev.length > 0) { + const last = prev[prev.length - 1]; + const dist = Math.hypot(p.x - last.x, p.y - last.y); + if (dist < MIN_DRAW_POINT_DISTANCE) return prev; + } + return [...prev, p]; + }); + } + + const canCount = points.length >= 2; + const currentCount = canCount + ? (mode === "filled" ? countFilledCells(points, cellSize) : countLineCells(points, cellSize, closed)).count + : 0; + + function handleReveal() { + setRevealed(true); + setSamples((prev) => + prev.some((s) => s.cellSize === cellSize) ? prev : [...prev, { cellSize, count: currentCount }], + ); + } + + function handleHalve() { + setStepIndex((i) => Math.min(i + 1, CELL_SIZE_STEPS.length - 1)); + setRevealed(false); + } + + const overallFactor = aggregateFactor(samples); + const isLastStep = stepIndex === CELL_SIZE_STEPS.length - 1; + + return ( +
+ {variant === "shapes" && ( +
+ {DEMO_SHAPES.map((s) => ( + + ))} +
+ )} + + + + {shapeId === "draw" && ( +
+ + {points.length < 2 && Mal mit dem Finger eine Linie auf das Feld.} +
+ )} + + {canCount && ( + <> + {stepIndex === 0 && !revealed && ( +

Schätz zuerst: Wie viele Kästchen berührt die Form wohl?

+ )} + +
+ {!revealed && ( + + )} + {revealed && !isLastStep && ( + + )} + {revealed && isLastStep && Kleinste Kästchengröße erreicht.} +
+ + {revealed &&

{currentCount} Kästchen

} + + + + {overallFactor !== null && } + + )} +
+ ); +} + +export default KaestchenSpiel; diff --git a/website/components/blog/box-dimension/KuestenSpiel.tsx b/website/components/blog/box-dimension/KuestenSpiel.tsx new file mode 100644 index 000000000..15c81b050 --- /dev/null +++ b/website/components/blog/box-dimension/KuestenSpiel.tsx @@ -0,0 +1,139 @@ +import React, { useState } from "react"; +import { css } from "../../../styled-system/css"; +import { BoxCanvas } from "./BoxCanvas"; +import { DimensionSkala, aggregateFactor, type HalvingSample } from "./DimensionSkala"; +import { Messreihe } from "./Messreihe"; +import { countLineCells, type Point } from "./boxCounting"; +import { COAST_REGIONS } from "./coasts"; + +const CELL_SIZE_STEPS = [25, 12.5, 6.25, 3.125, 1.5625]; + +type RegionId = keyof typeof COAST_REGIONS; +const REGION_IDS = Object.keys(COAST_REGIONS) as RegionId[]; +const REGION_LABELS: Record = { + bretagne: "Bretagne", + normandie: "Normandie", +}; + +const wrapper = css({ my: "6", p: "4", border: "1px solid token(colors.border, #e5e7eb)", borderRadius: "md" }); +const regionRow = css({ display: "flex", flexWrap: "wrap", gap: "2", mb: "3" }); + +function regionButtonStyle(active: boolean) { + return css({ + padding: "6px 12px", + borderRadius: "sm", + border: "1px solid token(colors.border, #e5e7eb)", + backgroundColor: active ? "explore" : "white", + color: active ? "white" : "gray.700", + fontSize: "sm", + fontWeight: active ? "bold" : "normal", + cursor: "pointer", + }); +} + +function actionButtonStyle(disabled: boolean) { + return css({ + padding: "8px 16px", + borderRadius: "sm", + border: "none", + backgroundColor: disabled ? "gray.300" : "explore", + color: "white", + fontSize: "sm", + fontWeight: "bold", + cursor: disabled ? "not-allowed" : "pointer", + }); +} + +const controlsRow = css({ display: "flex", gap: "2", alignItems: "center", flexWrap: "wrap", mt: "3" }); +const bigNumber = css({ fontSize: "2xl", fontWeight: "bold", color: "explore", mt: "3" }); +const hint = css({ fontSize: "sm", color: "gray.600", fontStyle: "italic", mt: "2" }); + +export function KuestenSpiel() { + const [regionId, setRegionId] = useState(REGION_IDS[0]); + const [stepIndex, setStepIndex] = useState(0); + const [revealed, setRevealed] = useState(false); + const [samples, setSamples] = useState([]); + + const region = COAST_REGIONS[regionId]; + const points: Point[] = region.points.map(([x, y]) => ({ x, y })); + const cellSize = CELL_SIZE_STEPS[stepIndex]; + + function resetProgress() { + setStepIndex(0); + setRevealed(false); + setSamples([]); + } + + function handleSelectRegion(id: RegionId) { + setRegionId(id); + resetProgress(); + } + + const currentCount = countLineCells(points, cellSize, false).count; + + function handleReveal() { + setRevealed(true); + setSamples((prev) => + prev.some((s) => s.cellSize === cellSize) ? prev : [...prev, { cellSize, count: currentCount }], + ); + } + + function handleHalve() { + setStepIndex((i) => Math.min(i + 1, CELL_SIZE_STEPS.length - 1)); + setRevealed(false); + } + + const overallFactor = aggregateFactor(samples); + const isLastStep = stepIndex === CELL_SIZE_STEPS.length - 1; + + return ( +
+
+ {REGION_IDS.map((id) => ( + + ))} +
+ + + + {stepIndex === 0 && !revealed && ( +

Schätz zuerst: Wie viele Kästchen berührt die Küste wohl?

+ )} + +
+ {!revealed && ( + + )} + {revealed && !isLastStep && ( + + )} + {revealed && isLastStep && Kleinste Kästchengröße erreicht.} +
+ + {revealed &&

{currentCount} Kästchen

} + + {immediateFactor !== null && ( +

+ Das ist das {immediateFactor.toFixed(1)}-fache von vorhin, nachdem die Kästchen halbiert wurden. +

+ )} + + {overallFactor !== null && } +
+ ); +} + +export default KuestenSpiel; diff --git a/website/components/blog/box-dimension/Messreihe.tsx b/website/components/blog/box-dimension/Messreihe.tsx new file mode 100644 index 000000000..d66aea1c4 --- /dev/null +++ b/website/components/blog/box-dimension/Messreihe.tsx @@ -0,0 +1,85 @@ +import React from "react"; +import { css } from "../../../styled-system/css"; +import { ESSAY_ACCENT } from "../palette"; +import type { HalvingSample } from "./DimensionSkala"; + +const wrapper = css({ mt: "3" }); + +const caption = css({ fontSize: "sm", color: "gray.600", mb: "1" }); + +const row = css({ + display: "flex", + alignItems: "center", + gap: "2", + mt: "1", +}); + +const rowLabel = css({ + fontSize: "sm", + color: "gray.700", + whiteSpace: "nowrap", + width: "90px", + flexShrink: 0, +}); + +const barTrack = css({ + flex: "1", + height: "18px", + bg: "gray.100", + borderRadius: "sm", + overflow: "hidden", +}); + +const barFill = css({ + height: "100%", + borderRadius: "sm", +}); + +const rowCount = css({ + fontSize: "sm", + fontWeight: "bold", + color: "explore", + width: "36px", + textAlign: "right", + flexShrink: 0, +}); + +export interface MessreiheProps { + /** Halving steps revealed so far, in any order — rendered coarsest-first. */ + samples: HalvingSample[]; + worldSize: number; +} + +/** + * Renders the halving steps taken so far as growing bars instead of a computed + * ratio sentence. "Kästchen pro Kante" (worldSize / cellSize) is always a clean + * integer for the widgets' own CELL_SIZE_STEPS, so no world-unit decimal ever + * reaches the child — they compare two whole numbers per row themselves. + */ +export function Messreihe({ samples, worldSize }: MessreiheProps) { + if (samples.length === 0) return null; + + const rows = [...samples].sort((a, b) => b.cellSize - a.cellSize); + const maxCount = Math.max(...rows.map((r) => r.count)); + + return ( +
+

Deine Messungen:

+ {rows.map((r) => { + const perEdge = Math.round(worldSize / r.cellSize); + const percent = maxCount > 0 ? (r.count / maxCount) * 100 : 0; + return ( +
+ {perEdge} pro Kante +
+
+
+ {r.count} +
+ ); + })} +
+ ); +} + +export default Messreihe; diff --git a/website/components/blog/box-dimension/WidgetPlaceholder.tsx b/website/components/blog/box-dimension/WidgetPlaceholder.tsx deleted file mode 100644 index c002527d6..000000000 --- a/website/components/blog/box-dimension/WidgetPlaceholder.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from "react"; -import { css } from "../../../styled-system/css"; - -const wrapper = css({ - my: "6", - p: "4", - border: "2px dashed token(colors.border, #e5e7eb)", - borderRadius: "md", - bg: "gray.50", - textAlign: "center", - color: "gray.600", - fontSize: "sm", -}); - -export interface WidgetPlaceholderProps { - /** Working title of the widget, shown as the placeholder heading. */ - label: string; - /** One-line description of what it will do, for the reviewer, not the reader. */ - description: string; -} - -/** - * Stand-in for an interactive widget that has not been implemented yet. - * Lets the surrounding post text be reviewed before the box-counting engine exists. - */ -export function WidgetPlaceholder({ label, description }: WidgetPlaceholderProps) { - return ( -
- 🚧 {label} -

{description}

-
- ); -} - -export default WidgetPlaceholder; diff --git a/website/components/blog/box-dimension/boxCounting.ts b/website/components/blog/box-dimension/boxCounting.ts new file mode 100644 index 000000000..70b0465ed --- /dev/null +++ b/website/components/blog/box-dimension/boxCounting.ts @@ -0,0 +1,170 @@ +/** + * Box-counting for the coast-dimension widgets. + * + * All geometry here lives in abstract world units (see each widget's `worldSize`), + * decoupled from CSS pixels and devicePixelRatio — DPR only enters when BoxCanvas + * draws to the screen, never in this counting math. That keeps the measured count + * independent of window size or screen density. + */ + +export interface Point { + x: number; + y: number; +} + +export interface CellCountResult { + count: number; + /** Cell keys as "col,row", for highlight rendering. */ + cells: string[]; +} + +interface CellIndex { + col: number; + row: number; +} + +function cellKey(col: number, row: number): string { + return `${col},${row}`; +} + +export function parseCellKey(key: string): [col: number, row: number] { + const [col, row] = key.split(",").map(Number); + return [col, row]; +} + +// Termination is driven by the segment's own parametric position (t in [0,1]), +// not by comparing against a separately-floored target cell index. That sidesteps +// the classic off-by-one ambiguity when an endpoint sits exactly on a grid line +// (e.g. a 40-unit square at cellSize=10) — floor(40/10) would otherwise claim a +// phantom extra column/row that the shape never actually reaches. +const EPS_T = 1e-9; + +/** + * Every grid cell a line segment passes through, via a 2D supercover/voxel + * traversal (Amanatides–Woo). Used instead of "round each sample point" because at + * small cell sizes a fixed sampling density can under-sample relative to cellSize, + * silently skipping cells the true segment crosses. + * + * `carried` lets consecutive segments of a polyline share one running cell index + * across their common vertex, instead of each independently re-flooring that + * vertex's raw coordinates — which is what actually resolves the boundary + * ambiguity for shapes like a closed square whose corners sit on grid lines. + */ +function traverseSegment(a: Point, b: Point, cellSize: number, into: Set, carried?: CellIndex): CellIndex { + const dx = b.x - a.x; + const dy = b.y - a.y; + + let col = carried?.col ?? Math.floor(a.x / cellSize); + let row = carried?.row ?? Math.floor(a.y / cellSize); + into.add(cellKey(col, row)); + + if (dx === 0 && dy === 0) { + return { col, row }; + } + + const stepCol = dx > 0 ? 1 : dx < 0 ? -1 : 0; + const stepRow = dy > 0 ? 1 : dy < 0 ? -1 : 0; + + const tDeltaX = dx !== 0 ? cellSize / Math.abs(dx) : Infinity; + const tDeltaY = dy !== 0 ? cellSize / Math.abs(dy) : Infinity; + + let tMaxX = dx !== 0 ? ((stepCol > 0 ? (col + 1) * cellSize : col * cellSize) - a.x) / dx : Infinity; + let tMaxY = dy !== 0 ? ((stepRow > 0 ? (row + 1) * cellSize : row * cellSize) - a.y) / dy : Infinity; + + const maxSteps = 100_000; // guard against pathological input + let steps = 0; + + while (Math.min(tMaxX, tMaxY) < 1 - EPS_T && steps < maxSteps) { + if (tMaxX < tMaxY) { + col += stepCol; + tMaxX += tDeltaX; + } else { + row += stepRow; + tMaxY += tDeltaY; + } + into.add(cellKey(col, row)); + steps += 1; + } + + return { col, row }; +} + +/** + * Cells intersected by an open or closed polyline (straight line, circle outline, + * coastline, hand-drawn curve — one code path for all of them). + */ +export function countLineCells(points: Point[], cellSize: number, closed = false): CellCountResult { + const hits = new Set(); + + if (points.length === 0 || cellSize <= 0) { + return { count: 0, cells: [] }; + } + + if (points.length === 1) { + const p = points[0]; + hits.add(cellKey(Math.floor(p.x / cellSize), Math.floor(p.y / cellSize))); + return { count: hits.size, cells: [...hits] }; + } + + let state: CellIndex | undefined; + for (let i = 0; i < points.length - 1; i++) { + state = traverseSegment(points[i], points[i + 1], cellSize, hits, state); + } + if (closed) { + traverseSegment(points[points.length - 1], points[0], cellSize, hits, state); + } + + return { count: hits.size, cells: [...hits] }; +} + +/** + * Cells covered by a filled polygon (only the square demo, to contrast the 4x + * growth of a dimension-2 shape against the 2x growth of a dimension-1 curve). + * Boundary cells come from countLineCells (handles partial edge coverage); the + * interior comes from a scanline pass per grid row using the even-odd rule. + * + * A far/upper bound (the last row, or the right end of a scanline span) that lands + * exactly on a grid line is nudged down by a tiny epsilon before flooring — such a + * boundary is the edge of the last cell the shape actually occupies, not the near + * edge of one beyond it. A near/lower bound needs no such nudge: floor() already + * assigns it to the correct cell. + */ +export function countFilledCells(polygon: Point[], cellSize: number): CellCountResult { + if (polygon.length < 3 || cellSize <= 0) { + return { count: 0, cells: [] }; + } + + const eps = cellSize * 1e-9; + const hits = new Set(countLineCells(polygon, cellSize, true).cells); + + const ys = polygon.map((p) => p.y); + const minRow = Math.floor(Math.min(...ys) / cellSize); + const maxRow = Math.floor((Math.max(...ys) - eps) / cellSize); + + for (let row = minRow; row <= maxRow; row++) { + const y = (row + 0.5) * cellSize; + const xs: number[] = []; + + for (let i = 0; i < polygon.length; i++) { + const a = polygon[i]; + const b = polygon[(i + 1) % polygon.length]; + const crosses = a.y <= y !== b.y <= y; + if (crosses) { + const t = (y - a.y) / (b.y - a.y); + xs.push(a.x + t * (b.x - a.x)); + } + } + + xs.sort((a, b) => a - b); + + for (let i = 0; i + 1 < xs.length; i += 2) { + const colStart = Math.floor(xs[i] / cellSize); + const colEnd = Math.floor((xs[i + 1] - eps) / cellSize); + for (let col = colStart; col <= colEnd; col++) { + hits.add(cellKey(col, row)); + } + } + } + + return { count: hits.size, cells: [...hits] }; +} diff --git a/website/components/blog/box-dimension/coasts.ts b/website/components/blog/box-dimension/coasts.ts new file mode 100644 index 000000000..f31ee9b27 --- /dev/null +++ b/website/components/blog/box-dimension/coasts.ts @@ -0,0 +1,1907 @@ +// GENERATED FILE — do not hand-edit. +// Source: notebooks/output/box_dimension_coasts.json +// Regenerate: cd notebooks && uv run python box_dimension_coastlines.py +// cd website && npm run build-coasts + +export interface CoastRegion { + name: string; + worldSize: number; + points: [number, number][]; + meta: { + sourceLengthKm: number; + simplificationToleranceDeg: number; + minCellSizeWorld: number; + minCellSizeRealKm: number; + boundingBox: { lonMin: number; lonMax: number; latMin: number; latMax: number }; + }; +} + +export const BRETAGNE: CoastRegion = { + name: "bretagne", + worldSize: 100, + points: [ + [76.634, 73.821], + [76.561, 73.278], + [75.982, 72.55], + [75.993, 72.352], + [76.162, 72.35], + [76.296, 72.081], + [76.331, 71.009], + [77.372, 70.682], + [78.222, 70.684], + [78.536, 70.502], + [78.631, 70.647], + [78.802, 70.464], + [79.434, 70.355], + [79.081, 70.229], + [79.458, 69.957], + [80.475, 69.995], + [81.032, 70.393], + [81.393, 70.467], + [80.994, 70.228], + [81.055, 70.101], + [82.316, 70.429], + [82.525, 70.272], + [82.582, 69.937], + [82.348, 69.525], + [80.789, 68.904], + [80.11, 68.902], + [79.166, 69.377], + [77.544, 69.449], + [76.332, 70.029], + [76.019, 69.667], + [75.885, 70.083], + [75.982, 70.191], + [75.933, 70.337], + [75.676, 70.392], + [75.484, 71.045], + [74.999, 71.01], + [74.745, 71.172], + [74.733, 71.482], + [73.91, 71.663], + [73.57, 72.28], + [72.675, 72.533], + [71.39, 71.553], + [70.979, 70.9], + [70.008, 70.464], + [69.378, 70.502], + [68.945, 70.719], + [69.196, 71.316], + [69.137, 71.48], + [68.215, 71.336], + [67.782, 70.937], + [67.466, 71.01], + [66.134, 70.21], + [65.396, 69.974], + [65.456, 69.666], + [66.133, 69.521], + [66.401, 69.667], + [66.714, 70.246], + [66.982, 70.32], + [67.079, 70.066], + [67.393, 70.211], + [67.259, 69.939], + [67.601, 69.829], + [67.393, 69.631], + [66.775, 69.54], + [66.86, 69.375], + [66.981, 69.484], + [67.466, 69.23], + [67.321, 68.941], + [67.176, 69.085], + [66.668, 69.086], + [66.328, 69.522], + [66.655, 68.743], + [66.559, 67.797], + [65.963, 66.835], + [65.188, 66.509], + [65.079, 66.308], + [65.335, 66.036], + [65.965, 65.818], + [66.789, 65.02], + [67.031, 65.02], + [67.129, 64.728], + [67.418, 64.693], + [67.733, 64.295], + [67.902, 64.73], + [68.047, 64.548], + [68.24, 64.695], + [68.739, 64.675], + [68.036, 64.096], + [68.193, 63.678], + [68.423, 63.658], + [68.072, 63.17], + [67.636, 63.098], + [67.345, 63.462], + [66.921, 63.078], + [66.776, 62.681], + [67.091, 62.245], + [67.041, 61.882], + [66.704, 61.409], + [66.836, 61.064], + [67.346, 61.065], + [67.66, 61.39], + [68.314, 61.101], + [69.452, 61.209], + [69.841, 60.775], + [70.081, 60.956], + [70.106, 60.737], + [69.768, 60.629], + [69.331, 61.03], + [68.411, 60.917], + [67.733, 60.41], + [66.739, 60.195], + [66.92, 59.848], + [66.837, 59.72], + [66.764, 60.085], + [66.376, 59.76], + [65.455, 59.94], + [65.467, 60.14], + [65.225, 60.32], + [65.31, 60.519], + [64.704, 60.23], + [64.027, 60.156], + [63.809, 60.193], + [63.591, 60.592], + [63.323, 60.737], + [62.875, 60.72], + [62.936, 60.484], + [63.057, 60.557], + [63.228, 60.302], + [63.421, 60.265], + [63.362, 60.429], + [63.493, 60.484], + [63.579, 60.067], + [63.458, 59.884], + [63.905, 59.613], + [64.147, 59.65], + [64.427, 59.232], + [64.571, 58.795], + [64.536, 58.668], + [64.293, 59.178], + [63.76, 59.65], + [63.349, 59.032], + [63.433, 59.377], + [63.325, 59.541], + [62.596, 59.467], + [63.24, 59.739], + [63.105, 60.194], + [62.017, 60.156], + [62.113, 59.975], + [61.482, 59.721], + [61.931, 60.138], + [61.882, 60.611], + [61.434, 61.138], + [60.708, 60.773], + [59.906, 60.666], + [59.786, 60.885], + [59.302, 61.065], + [58.503, 61.174], + [58.188, 61.574], + [56.712, 61.065], + [56.577, 60.938], + [56.698, 60.575], + [56.65, 60.321], + [56.346, 60.084], + [56.227, 59.758], + [55.401, 59.212], + [55.089, 59.395], + [54.954, 59.306], + [55.355, 58.922], + [54.773, 59.069], + [54.712, 58.904], + [54.857, 58.651], + [54.664, 58.434], + [54.82, 58.414], + [54.894, 58.126], + [54.99, 58.305], + [55.184, 58.161], + [55.257, 58.451], + [55.427, 58.234], + [55.62, 58.488], + [55.511, 58.142], + [55.79, 58.053], + [55.814, 58.488], + [56.069, 58.396], + [55.924, 58.759], + [56.322, 58.633], + [56.358, 58.942], + [56.237, 59.086], + [56.517, 59.248], + [56.723, 59.16], + [56.505, 59.087], + [56.662, 58.816], + [56.976, 58.961], + [57.294, 58.705], + [57.207, 58.941], + [57.28, 59.23], + [57.341, 59.288], + [57.329, 59.013], + [57.499, 58.977], + [57.474, 58.723], + [57.327, 58.65], + [57.365, 58.414], + [57.872, 58.453], + [57.861, 59.124], + [58.091, 59.215], + [58.151, 59.013], + [58.056, 58.868], + [58.334, 58.633], + [58.527, 58.85], + [58.733, 58.83], + [58.623, 59.141], + [59.037, 58.996], + [59.131, 59.25], + [59.302, 59.285], + [59.763, 58.996], + [60.005, 59.031], + [59.921, 58.65], + [60.162, 58.287], + [60.115, 57.962], + [60.43, 57.707], + [60.38, 57.163], + [60.611, 57.073], + [60.659, 57.364], + [60.878, 57.362], + [60.72, 57.307], + [60.852, 56.964], + [61.023, 57.144], + [61.095, 56.962], + [61.252, 56.981], + [61.106, 56.763], + [61.253, 56.689], + [61.203, 56.218], + [61.349, 56.074], + [60.804, 54.859], + [60.817, 55.24], + [61.01, 55.419], + [61.131, 55.82], + [60.951, 56.275], + [60.683, 56.02], + [60.622, 56.037], + [60.889, 56.327], + [60.854, 56.528], + [60.598, 56.727], + [60.661, 56.926], + [60.418, 56.927], + [60.318, 56.454], + [60.127, 56.528], + [60.053, 56.347], + [59.933, 56.345], + [60.066, 56.51], + [60.007, 56.6], + [59.255, 55.911], + [58.636, 55.675], + [58.698, 55.438], + [58.841, 55.439], + [59.969, 55.928], + [59.726, 55.71], + [59.752, 55.457], + [59.886, 55.368], + [59.667, 55.293], + [59.606, 55.675], + [59.459, 55.638], + [59.291, 55.276], + [59.387, 54.948], + [59.24, 54.803], + [59.254, 54.532], + [59.143, 55.166], + [58.866, 55.258], + [58.429, 55.111], + [58.346, 54.947], + [58.54, 54.694], + [58.309, 54.82], + [58.116, 54.459], + [58.223, 54.949], + [58.637, 55.349], + [58.553, 55.62], + [58.093, 55.802], + [57.533, 55.62], + [57.097, 56.02], + [57.012, 55.964], + [57.193, 55.693], + [57.003, 55.658], + [57.039, 55.783], + [56.929, 55.837], + [56.445, 55.765], + [56.467, 55.62], + [56.325, 55.584], + [56.069, 55.747], + [56.383, 55.857], + [56.117, 56.218], + [56.311, 56.436], + [56.227, 56.71], + [55.476, 56.382], + [55.245, 56.728], + [55.463, 56.69], + [55.355, 57.328], + [55.112, 57.109], + [54.844, 57.29], + [54.955, 56.982], + [54.844, 56.889], + [54.555, 57.327], + [54.373, 56.946], + [54.567, 56.909], + [54.58, 56.456], + [54.361, 56.819], + [54.275, 56.364], + [54.374, 56.185], + [54.226, 56.254], + [54.083, 56.691], + [54.154, 57.127], + [54.071, 57.145], + [53.695, 56.147], + [53.817, 55.966], + [53.743, 55.566], + [54.106, 55.385], + [53.998, 55.258], + [53.475, 55.347], + [53.38, 55.093], + [53.475, 54.912], + [53.185, 54.84], + [53.259, 54.476], + [52.944, 54.116], + [52.93, 53.842], + [52.822, 54.151], + [52.92, 54.476], + [53.114, 54.658], + [53.136, 55.022], + [53.38, 55.166], + [53.598, 55.93], + [53.271, 56.019], + [53.355, 56.183], + [53.185, 56.256], + [53.575, 56.581], + [53.307, 57.017], + [53.415, 57.292], + [53.78, 57.436], + [53.817, 57.744], + [54.144, 58.232], + [54.349, 58.29], + [54.263, 58.523], + [54.094, 58.561], + [53.633, 58.451], + [53.464, 58.088], + [53.294, 58.561], + [52.956, 58.378], + [52.906, 58.161], + [53.113, 58.251], + [52.968, 57.961], + [53.04, 57.815], + [52.833, 57.507], + [52.485, 57.38], + [52.738, 57.038], + [52.448, 57.181], + [52.289, 56.982], + [52.568, 57.942], + [52.35, 57.761], + [52.301, 58.015], + [52.01, 58.016], + [51.708, 57.128], + [51.803, 56.618], + [51.612, 56.509], + [51.612, 56.076], + [51.443, 55.784], + [51.611, 55.53], + [51.332, 55.548], + [51.236, 54.896], + [51.162, 55.185], + [50.897, 55.185], + [50.885, 55.24], + [51.189, 55.219], + [51.248, 55.603], + [51.393, 55.709], + [51.514, 56.255], + [51.393, 56.509], + [51.514, 56.872], + [51.418, 56.944], + [51.647, 57.945], + [51.067, 57.688], + [51.199, 57.417], + [51.089, 57.255], + [51.067, 57.509], + [50.704, 57.544], + [50.677, 57.832], + [50.316, 57.726], + [49.418, 58.125], + [49.091, 57.633], + [49.178, 57.471], + [49.541, 57.579], + [49.528, 57.344], + [49.226, 57.399], + [49.275, 57.183], + [49.078, 57.183], + [48.523, 56.601], + [48.461, 57.634], + [48.873, 57.561], + [48.486, 58.216], + [48.39, 58.978], + [48.486, 59.921], + [48.608, 60.356], + [48.801, 60.465], + [48.802, 60.973], + [49.031, 61.319], + [49.541, 61.646], + [49.6, 61.989], + [49.891, 62.208], + [49.782, 62.26], + [48.838, 61.899], + [48.427, 62.044], + [48.075, 61.626], + [48.075, 61.263], + [47.833, 60.755], + [47.904, 60.539], + [47.807, 59.994], + [47.542, 59.704], + [48.015, 59.576], + [48.293, 58.868], + [48.195, 57.309], + [47.639, 56.291], + [47.857, 56.074], + [47.505, 56.128], + [46.681, 55.547], + [46.416, 55.548], + [46.452, 55.277], + [46.161, 54.802], + [46.38, 54.15], + [46.257, 54.041], + [46.246, 53.732], + [46.839, 53.859], + [46.706, 53.623], + [46.512, 53.733], + [46.184, 53.57], + [46.536, 53.406], + [46.525, 53.206], + [46.658, 53.079], + [46.803, 53.15], + [46.925, 52.642], + [47.043, 52.861], + [47.602, 53.044], + [47.542, 52.808], + [47.651, 52.643], + [47.82, 52.79], + [48.109, 52.282], + [48.449, 52.209], + [48.872, 52.37], + [48.643, 51.955], + [48.233, 52.172], + [48.037, 52.063], + [48.063, 52.244], + [47.773, 52.245], + [47.807, 52.406], + [47.675, 52.462], + [47.347, 52.299], + [47.942, 51.991], + [47.88, 51.72], + [47.966, 51.408], + [48.123, 51.429], + [48.087, 51.775], + [48.572, 51.917], + [48.487, 51.503], + [48.849, 51.32], + [48.595, 51.19], + [48.595, 51.046], + [49.238, 51.354], + [49.031, 50.974], + [48.573, 51.009], + [48.426, 51.229], + [47.749, 51.337], + [47.688, 51.683], + [47.555, 51.736], + [47.579, 51.265], + [47.82, 51.264], + [48.109, 50.756], + [48.255, 50.938], + [48.317, 50.81], + [48.257, 50.612], + [47.989, 50.829], + [47.88, 50.702], + [48.39, 50.121], + [48.04, 50.32], + [47.965, 49.886], + [47.88, 50.304], + [47.664, 50.593], + [47.806, 50.702], + [47.698, 50.864], + [47.334, 50.792], + [47.286, 50.504], + [46.875, 50.503], + [46.791, 50.304], + [46.865, 50.195], + [46.67, 50.12], + [46.742, 50.774], + [47.068, 50.575], + [47.324, 50.992], + [47.177, 51.209], + [47.373, 51.574], + [47.253, 51.753], + [47.492, 51.937], + [47.19, 52.172], + [46.972, 51.919], + [46.657, 52.029], + [46.536, 51.701], + [46.621, 52.118], + [46.355, 52.263], + [46.561, 52.427], + [46.705, 52.352], + [46.706, 52.606], + [46.296, 52.682], + [46.1, 52.354], + [46.016, 52.409], + [46.259, 52.699], + [46.148, 52.861], + [46.45, 52.918], + [46.233, 53.242], + [46.246, 53.443], + [46.004, 53.369], + [45.919, 53.606], + [46.137, 54.113], + [46.003, 54.676], + [44.719, 53.37], + [44.211, 53.044], + [42.732, 52.426], + [42.103, 52.426], + [41.836, 52.825], + [41.727, 52.626], + [41.983, 52.135], + [42.151, 52.28], + [43.025, 52.245], + [44.067, 52.791], + [43.873, 52.209], + [43.41, 52.136], + [43.255, 51.936], + [43.436, 51.701], + [43.001, 51.954], + [42.2, 51.807], + [42.03, 52.063], + [41.679, 51.863], + [41.885, 51.628], + [42.031, 51.665], + [42.054, 51.482], + [42.261, 51.464], + [42.285, 51.32], + [42.115, 51.173], + [42.262, 50.992], + [42.115, 50.883], + [42.2, 50.611], + [42.49, 50.901], + [42.757, 50.792], + [42.732, 50.611], + [42.951, 50.647], + [43.193, 50.139], + [43.399, 50.011], + [43.701, 49.086], + [44.004, 49.068], + [44.067, 48.65], + [43.726, 48.58], + [43.956, 48.777], + [43.896, 49.051], + [43.605, 48.942], + [43.412, 49.377], + [43.049, 49.704], + [42.829, 49.667], + [42.868, 50.048], + [42.539, 50.283], + [42.297, 50.14], + [42.298, 50.357], + [42.177, 50.285], + [42.115, 49.829], + [42.007, 49.776], + [42.093, 50.231], + [41.945, 50.266], + [42.042, 50.375], + [41.739, 51.011], + [41.644, 51.046], + [41.618, 50.865], + [41.424, 50.975], + [41.352, 50.757], + [40.625, 50.683], + [41.304, 50.974], + [41.389, 51.281], + [41.559, 51.392], + [41.085, 52.099], + [40.165, 52.136], + [39.827, 51.991], + [39.099, 52.389], + [38.847, 52.226], + [38.905, 51.989], + [38.227, 51.483], + [38.203, 51.155], + [37.73, 50.884], + [37.779, 50.703], + [37.537, 50.193], + [36.881, 49.36], + [36.981, 49.177], + [36.859, 48.924], + [36.884, 48.633], + [37.028, 48.488], + [36.969, 47.998], + [36.835, 48.198], + [36.907, 48.416], + [36.786, 48.74], + [36.811, 49.216], + [36.604, 49.558], + [35.927, 49.195], + [34.789, 49.197], + [34.497, 49.051], + [34.424, 49.197], + [33.503, 48.942], + [33.431, 48.542], + [33.093, 48.725], + [32.802, 48.506], + [32.679, 48.87], + [32.534, 48.869], + [31.264, 47.873], + [31.638, 47.309], + [31.905, 47.344], + [31.881, 47.163], + [32.208, 47.001], + [32.039, 46.964], + [32.05, 46.618], + [31.736, 47.237], + [30.984, 47.706], + [30.657, 47.11], + [30.608, 46.638], + [30.488, 46.42], + [30.524, 46.039], + [30.198, 46.31], + [30.318, 46.71], + [30.55, 46.727], + [30.61, 47.001], + [30.271, 47.146], + [30.658, 47.216], + [30.875, 47.762], + [30.717, 47.963], + [30.233, 48.217], + [30.016, 48.109], + [29.045, 48.361], + [28.44, 48.035], + [27.957, 47.999], + [27.352, 48.323], + [27.714, 47.636], + [27.521, 47.816], + [26.429, 46.545], + [26.078, 46.347], + [26.164, 45.784], + [26.285, 45.747], + [26.333, 45.929], + [26.516, 45.693], + [26.271, 45.659], + [26.465, 45.403], + [26.018, 45.676], + [25.569, 45.369], + [25.654, 45.24], + [26.055, 45.295], + [26.081, 45.151], + [25.873, 45.13], + [25.788, 44.896], + [25.872, 44.476], + [25.63, 44.84], + [25.269, 44.767], + [25.134, 44.642], + [25.11, 44.276], + [24.746, 43.987], + [24.869, 43.734], + [24.699, 43.806], + [24.711, 43.353], + [24.59, 43.752], + [24.08, 43.68], + [23.971, 43.624], + [24.092, 43.443], + [23.887, 43.426], + [23.839, 43.17], + [23.729, 43.443], + [23.765, 43.754], + [23.475, 43.642], + [23.45, 43.461], + [23.318, 43.553], + [23.572, 43.898], + [23.873, 43.916], + [23.535, 44.132], + [23.634, 45.076], + [23.839, 45.532], + [23.401, 45.423], + [23.185, 45.601], + [22.023, 45.746], + [22.24, 45.567], + [23.21, 45.53], + [23.292, 45.439], + [22.747, 45.458], + [22.675, 45.239], + [22.53, 45.493], + [22.239, 45.495], + [22.264, 45.347], + [21.998, 45.167], + [22.131, 45.548], + [21.902, 45.895], + [21.441, 45.421], + [20.616, 45.132], + [21.196, 45.203], + [21.816, 45.622], + [21.319, 45.167], + [21.247, 44.914], + [21.052, 44.985], + [21.077, 44.66], + [20.908, 44.879], + [20.811, 44.442], + [20.725, 44.714], + [20.872, 44.968], + [20.349, 45.168], + [20.181, 44.804], + [19.804, 44.64], + [19.949, 44.459], + [19.756, 44.424], + [20.048, 44.279], + [19.648, 44.115], + [19.563, 43.877], + [19.672, 43.716], + [19.38, 43.716], + [19.113, 43.498], + [18.897, 42.916], + [18.896, 43.571], + [18.509, 43.714], + [18.217, 43.568], + [18.122, 43.279], + [18.037, 43.408], + [18.195, 43.751], + [18.461, 43.824], + [19.091, 43.68], + [19.636, 44.314], + [19.877, 45.077], + [19.381, 45.094], + [18.193, 45.894], + [18.205, 45.657], + [18.641, 45.15], + [18.267, 45.384], + [18.013, 44.749], + [18.134, 44.568], + [17.541, 44.043], + [17.844, 44.605], + [17.479, 44.64], + [17.575, 44.861], + [17.431, 45.077], + [17.444, 45.313], + [17.154, 44.877], + [17.165, 45.003], + [17.672, 45.695], + [17.66, 45.891], + [17.781, 46.001], + [17.856, 45.785], + [18.037, 46.021], + [17.71, 46.219], + [17.951, 46.366], + [18.315, 46.255], + [18.594, 46.458], + [18.423, 46.564], + [18.327, 47.255], + [18.097, 47.346], + [18.086, 47.618], + [17.904, 47.781], + [17.565, 48.034], + [17.04, 47.888], + [16.991, 47.398], + [16.911, 47.344], + [16.994, 47.836], + [16.791, 48.18], + [15.432, 48.324], + [15.093, 48.215], + [15.118, 48.07], + [14.972, 48.217], + [14.656, 48.18], + [14.439, 47.852], + [13.156, 47.927], + [12.671, 48.108], + [12.236, 47.999], + [12.345, 47.471], + [12.076, 46.926], + [12.526, 46.62], + [12.986, 46.618], + [13.07, 46.346], + [12.828, 44.786], + [12.32, 43.261], + [11.787, 42.211], + [10.466, 40.413], + [9.959, 39.903], + [9.255, 39.685], + [9.231, 39.47], + [8.638, 39.028], + [7.547, 38.666], + [7.643, 37.903], + [7.535, 37.957], + [7.584, 38.285], + [7.45, 38.629], + [7.037, 38.665], + [6.918, 39.028], + [6.626, 39.029], + [5.874, 38.81], + [5.755, 38.447], + [5.052, 38.34], + [4.76, 37.865], + [4.397, 38.119], + [2.921, 37.975], + [2.533, 37.685], + [2.023, 37.687], + [1.673, 37.341], + [2.641, 37.124], + [2.398, 36.761], + [2.363, 36.196], + [2.896, 36.416], + [3.089, 36.197], + [3.404, 36.415], + [3.585, 36.326], + [3.718, 35.943], + [4.784, 36.088], + [4.808, 35.834], + [5.1, 36.017], + [6.407, 35.472], + [7.039, 35.691], + [7.475, 35.182], + [7.814, 35.29], + [7.934, 35.108], + [8.516, 35.072], + [8.902, 35.291], + [9.267, 34.89], + [9.581, 34.783], + [10.647, 34.71], + [10.915, 34.347], + [11.738, 34.456], + [12.488, 34.273], + [12.778, 34.529], + [13.166, 34.565], + [13.36, 35.072], + [13.41, 34.745], + [13.627, 34.709], + [13.7, 34.963], + [14.355, 35.109], + [14.729, 34.51], + [14.923, 33.892], + [14.778, 33.529], + [15.239, 33.24], + [15.069, 32.26], + [14.719, 32.241], + [14.487, 32.041], + [14.608, 31.387], + [14.27, 30.554], + [13.965, 30.425], + [13.895, 30.209], + [12.272, 30.062], + [12.357, 29.791], + [12.054, 29.519], + [11.737, 29.591], + [11.058, 29.337], + [11.011, 29.157], + [10.55, 29.158], + [10.622, 28.721], + [10.395, 28.775], + [10.552, 28.939], + [9.51, 28.685], + [8.588, 28.974], + [8.529, 29.211], + [8.771, 29.393], + [8.722, 29.573], + [8.297, 29.808], + [8.214, 30.263], + [8.02, 30.518], + [8.067, 30.698], + [7.438, 31.244], + [7.401, 31.734], + [6.991, 31.733], + [6.832, 31.353], + [7.1, 31.205], + [6.953, 30.952], + [7.124, 30.882], + [7.027, 30.772], + [7.05, 29.862], + [6.784, 29.499], + [6.856, 29.248], + [6.724, 28.939], + [6.445, 28.957], + [6.625, 28.648], + [7.233, 28.537], + [7.22, 28.05], + [6.869, 27.777], + [6.24, 28.175], + [5.682, 28.104], + [5.731, 27.922], + [5.367, 27.703], + [5.149, 27.743], + [5.075, 27.995], + [4.908, 27.959], + [5.161, 27.105], + [4.774, 26.78], + [5.295, 26.833], + [5.538, 26.542], + [5.631, 26.761], + [5.946, 26.833], + [5.718, 26.852], + [5.851, 27.013], + [6.092, 27.016], + [6.553, 26.688], + [6.541, 26.126], + [6.396, 26.054], + [6.397, 25.365], + [6.227, 25.11], + [6.492, 24.819], + [6.482, 24.656], + [6.748, 24.656], + [6.965, 24.331], + [7.594, 24.111], + [7.56, 24.493], + [7.316, 24.891], + [7.292, 25.473], + [6.979, 25.98], + [7.135, 26.291], + [7.451, 26.255], + [7.473, 26.651], + [7.909, 26.434], + [7.824, 26.272], + [7.91, 26.106], + [8.478, 26.524], + [8.273, 26.835], + [9.267, 26.616], + [9.606, 26.399], + [9.679, 26.181], + [9.921, 26.362], + [10.962, 26.288], + [10.925, 26.561], + [11.146, 26.78], + [11.107, 26.943], + [11.666, 27.087], + [12.514, 26.904], + [12.878, 26.615], + [13.288, 26.615], + [14.016, 26.107], + [15.105, 26.181], + [15.384, 26.38], + [15.359, 26.67], + [14.863, 26.471], + [14.803, 27.031], + [14.705, 27.105], + [14.983, 27.123], + [15.65, 27.578], + [15.276, 27.813], + [15.685, 27.741], + [15.952, 28.103], + [16.219, 28.141], + [16.437, 28.102], + [16.657, 27.813], + [17.044, 28.575], + [17.019, 28.247], + [17.769, 28.357], + [17.841, 28.504], + [17.916, 28.357], + [18.545, 28.358], + [18.605, 28.23], + [17.09, 28.175], + [16.947, 27.778], + [16.751, 27.669], + [16.17, 28.032], + [15.759, 27.414], + [14.983, 26.977], + [14.959, 26.723], + [15.443, 26.907], + [15.904, 26.653], + [16.085, 26.451], + [16.049, 26.287], + [16.607, 26.435], + [16.728, 26.251], + [16.922, 26.397], + [17.067, 26.216], + [17.735, 26.126], + [16.487, 26.106], + [16.243, 25.998], + [16.062, 25.69], + [16.438, 25.49], + [16.51, 25.381], + [16.05, 25.563], + [15.928, 25.998], + [15.493, 25.6], + [14.923, 25.438], + [15.056, 25.198], + [15.663, 24.982], + [15.856, 24.8], + [15.179, 25.127], + [14.862, 25.089], + [14.67, 25.489], + [13.3, 25.29], + [13.652, 24.947], + [13.845, 25.018], + [13.688, 24.856], + [13.773, 24.728], + [14.136, 24.729], + [14.269, 24.637], + [13.869, 24.691], + [13.845, 24.437], + [14.79, 24.148], + [14.946, 24.021], + [14.621, 23.892], + [14.644, 23.785], + [15.251, 23.713], + [15.347, 23.529], + [14.96, 23.64], + [14.789, 23.531], + [14.692, 23.711], + [14.498, 23.566], + [14.283, 23.639], + [14.197, 23.875], + [14.366, 23.984], + [14.258, 24.147], + [13.532, 24.291], + [13.469, 23.912], + [13.772, 23.384], + [13.095, 24.11], + [12.803, 24.111], + [12.815, 23.911], + [13.022, 23.82], + [13.167, 23.529], + [12.562, 24.003], + [12.33, 24.385], + [12.405, 24.566], + [12.332, 24.783], + [12.175, 24.91], + [11.024, 24.891], + [11.277, 24.436], + [11.641, 24.183], + [10.283, 24.835], + [9.909, 24.783], + [10.054, 24.673], + [10.064, 24.33], + [10.441, 24.094], + [10.199, 23.949], + [10.442, 23.477], + [10.404, 23.312], + [10.939, 22.987], + [11.495, 22.222], + [12.174, 21.969], + [12.705, 21.534], + [13.215, 21.46], + [14.403, 20.626], + [14.428, 20.481], + [13.071, 21.316], + [12.632, 21.245], + [12.368, 21.645], + [12.173, 21.643], + [12.186, 21.479], + [11.957, 21.279], + [12.113, 21.733], + [11.666, 21.824], + [11.471, 22.116], + [10.89, 21.823], + [10.551, 21.859], + [10.187, 22.514], + [9.993, 22.405], + [9.775, 22.514], + [9.836, 22.388], + [9.703, 22.477], + [9.765, 22.315], + [9.654, 22.406], + [9.678, 22.262], + [9.34, 22.551], + [9.048, 22.44], + [9.024, 22.66], + [8.711, 22.514], + [8.661, 22.258], + [8.661, 22.625], + [8.468, 22.586], + [7.618, 23.494], + [7.061, 23.386], + [6.893, 23.675], + [6.724, 23.604], + [6.506, 23.894], + [6.141, 23.967], + [5.389, 24.475], + [5.247, 24.439], + [5.22, 24.257], + [4.834, 24.365], + [4.52, 24.003], + [4.252, 24.077], + [4.035, 23.819], + [3.355, 23.603], + [2.775, 23.857], + [2.714, 24.02], + [2.811, 24.347], + [2.507, 24.691], + [1.66, 24.581], + [1.2, 24.8], + [0.667, 24.691], + [0.581, 24.347], + [0.728, 23.982], + [0.363, 23.658], + [0.425, 23.386], + [0.799, 23.332], + [0.231, 23.312], + [0.17, 23.04], + [0.715, 23.132], + [0.971, 22.857], + [0.97, 22.604], + [0.824, 22.461], + [0.922, 22.385], + [0.667, 22.332], + [0.558, 22.061], + [0.605, 21.481], + [0.279, 21.426], + [0.256, 21.172], + [0, 21.006], + [0.388, 20.355], + [0.266, 20.103], + [0.533, 19.703], + [0.34, 19.665], + [0.702, 19.159], + [0.715, 18.848], + [0.837, 18.884], + [1.005, 18.631], + [1.162, 18.723], + [1.103, 18.958], + [1.248, 18.521], + [1.818, 18.432], + [1.272, 18.377], + [0.981, 18.558], + [0.677, 18.177], + [0.716, 18.014], + [0.535, 17.924], + [0.677, 17.851], + [0.484, 17.414], + [0.582, 17.199], + [0.508, 17.051], + [0.8, 16.835], + [0.752, 16.546], + [0.971, 16.363], + [0.872, 16.325], + [0.995, 16.144], + [0.872, 15.925], + [1.126, 15.838], + [1.296, 15.981], + [1.259, 15.782], + [1.357, 15.673], + [1.258, 15.419], + [1.732, 15.112], + [1.926, 14.747], + [2.266, 14.712], + [2.264, 14.893], + [2.483, 14.819], + [2.581, 15.038], + [2.724, 14.893], + [2.592, 14.764], + [2.689, 14.403], + [2.581, 14.167], + [2.943, 14.274], + [3.185, 14.132], + [3.175, 14.328], + [3.284, 14.382], + [3.514, 14.259], + [3.574, 14.021], + [4.131, 14.203], + [4.81, 13.912], + [5.755, 14.203], + [5.973, 14.818], + [6.383, 14.71], + [6.02, 14.529], + [5.875, 14.095], + [5.306, 13.931], + [5.572, 13.64], + [5.559, 13.476], + [5.404, 13.459], + [5.525, 13.278], + [5.452, 13.023], + [5.706, 12.606], + [6.058, 12.914], + [5.851, 13.186], + [7.329, 13.005], + [7.838, 13.26], + [7.231, 12.824], + [6.869, 12.787], + [6.964, 12.424], + [6.566, 12.515], + [6.663, 12.262], + [6.493, 12.153], + [6.638, 12.043], + [6.589, 11.825], + [7.306, 11.843], + [7.147, 11.428], + [7.376, 11.261], + [7.485, 11.317], + [7.39, 11.535], + [7.547, 11.624], + [8.079, 11.409], + [8.237, 11.571], + [8.164, 11.789], + [8.273, 11.953], + [9.606, 11.733], + [10.164, 11.19], + [10.332, 11.19], + [10.405, 11.479], + [10.6, 11.155], + [11.253, 11.261], + [10.526, 10.862], + [10.417, 10.518], + [11.638, 10.464], + [12.174, 9.956], + [13.046, 9.52], + [13.553, 9.665], + [13.495, 9.938], + [13.602, 10.028], + [14.014, 9.774], + [14.136, 10.028], + [14.451, 10.101], + [14.512, 10.301], + [14.367, 10.446], + [14.389, 10.63], + [13.859, 10.954], + [14.101, 11.028], + [13.931, 11.207], + [13.99, 11.335], + [14.329, 11.481], + [14.535, 11.353], + [14.401, 11.189], + [14.863, 11.081], + [15.128, 10.754], + [15.881, 10.754], + [16.46, 10.538], + [16.584, 10.535], + [16.425, 10.662], + [16.63, 10.792], + [17.357, 10.645], + [16.644, 10.373], + [16.896, 9.884], + [17.744, 9.122], + [17.892, 9.229], + [19.224, 8.758], + [20.046, 8.868], + [20.193, 8.757], + [20.349, 8.849], + [20.335, 9.157], + [20.482, 8.902], + [20.578, 9.122], + [20.748, 8.866], + [20.784, 9.139], + [21.136, 9.304], + [21.086, 9.449], + [21.354, 9.667], + [21.076, 9.174], + [21.56, 9.175], + [21.367, 9.031], + [21.51, 8.485], + [21.936, 8.394], + [22.033, 7.997], + [22.616, 8.032], + [22.93, 7.344], + [23.061, 7.396], + [22.917, 7.687], + [22.991, 7.943], + [23.208, 8.076], + [23.414, 7.876], + [23.404, 7.492], + [23.741, 7.421], + [23.656, 7.584], + [23.815, 7.639], + [24.021, 7.476], + [24.093, 7.802], + [23.874, 8.708], + [24.359, 9.18], + [24.067, 9.109], + [24.093, 9.616], + [23.971, 9.797], + [24.516, 9.708], + [24.6, 9.871], + [24.48, 10.015], + [24.577, 10.379], + [24.467, 10.978], + [24.832, 10.361], + [25.122, 10.47], + [25.389, 9.708], + [25.775, 9.962], + [26.115, 9.815], + [26.345, 10.053], + [25.91, 10.305], + [26.005, 10.706], + [26.26, 11.088], + [27.23, 11.558], + [27.351, 12.066], + [27.631, 11.76], + [27.073, 9.835], + [27.325, 9.779], + [27.388, 10.234], + [27.57, 10.359], + [27.532, 9.434], + [27.314, 9.255], + [27.495, 8.872], + [27.775, 8.71], + [27.738, 8.327], + [27.884, 8.328], + [28.052, 8.037], + [28.187, 8.491], + [28.32, 8.545], + [28.479, 8.165], + [28.392, 7.857], + [28.732, 8.11], + [28.975, 8.037], + [29.336, 8.546], + [30.161, 8.22], + [31.228, 8.364], + [31.36, 8.454], + [31.324, 8.619], + [31.809, 9.017], + [32.245, 8.981], + [32.413, 9.198], + [33.091, 8.765], + [33.333, 8.907], + [33.479, 8.764], + [33.177, 9.398], + [33.588, 9.798], + [33.238, 10.107], + [33.672, 9.924], + [33.661, 9.434], + [33.988, 9.235], + [34.302, 9.307], + [34.472, 9.853], + [34.861, 9.962], + [35.417, 9.744], + [35.672, 9.364], + [35.235, 9.036], + [35.249, 7.494], + [35.467, 7.604], + [35.755, 7.422], + [35.781, 7.238], + [36.242, 7.421], + [36.58, 7.384], + [36.557, 7.202], + [37.028, 7.222], + [36.218, 7.168], + [36.206, 6.568], + [35.78, 6.042], + [35.2, 6.151], + [35.356, 5.806], + [35.09, 5.623], + [35.236, 5.516], + [35.21, 5.153], + [35.38, 5.006], + [35.187, 4.755], + [35.611, 4.699], + [35.549, 4.536], + [35.709, 4.372], + [35.998, 4.626], + [36.411, 4.41], + [36.617, 4.028], + [36.555, 3.827], + [36.314, 3.865], + [36.277, 3.627], + [36.495, 3.556], + [36.508, 3.21], + [37.113, 3.464], + [36.906, 3.01], + [37.281, 2.702], + [37.464, 2.902], + [37.392, 3.046], + [37.599, 3.21], + [37.623, 2.918], + [38.059, 3.137], + [37.95, 2.939], + [38.022, 2.757], + [38.3, 2.665], + [38.529, 3.194], + [38.93, 3.609], + [39.779, 3.427], + [39.837, 3.554], + [39.584, 3.755], + [39.438, 4.153], + [39.33, 4.138], + [39.367, 4.407], + [40.77, 4.226], + [40.903, 4.099], + [40.868, 3.609], + [41.014, 3.683], + [41.691, 3.356], + [41.959, 3.429], + [42.83, 2.591], + [43.485, 2.739], + [43.702, 2.957], + [43.969, 2.414], + [44.185, 2.375], + [44.186, 2.629], + [44.478, 2.774], + [44.732, 2.431], + [44.743, 2.157], + [45.312, 1.523], + [45.253, 1.359], + [45.641, 1.213], + [45.845, 1.414], + [46.162, 2.213], + [45.799, 2.721], + [46.307, 2.685], + [46.052, 4.008], + [46.377, 3.63], + [46.404, 3.192], + [46.621, 2.646], + [46.877, 2.375], + [47.046, 2.376], + [47.094, 2.63], + [47.286, 1.978], + [47.772, 1.977], + [48.305, 1.723], + [48.45, 1.431], + [49.031, 1.357], + [49.153, 1.141], + [49.444, 1.178], + [50.025, 0.633], + [50.109, 0.69], + [49.953, 0.961], + [49.333, 1.377], + [49.759, 1.287], + [49.963, 1.705], + [49.88, 1.832], + [49.686, 1.579], + [49.455, 1.668], + [49.627, 1.812], + [49.745, 2.285], + [49.358, 2.865], + [49.638, 2.81], + [49.891, 3.011], + [49.746, 3.119], + [49.648, 3.738], + [49.287, 4.099], + [49.455, 4.099], + [49.212, 4.753], + [49.333, 4.97], + [48.558, 5.516], + [48.656, 5.588], + [48.584, 6.095], + [48.328, 6.442], + [48.692, 6.222], + [48.815, 5.824], + [49.334, 5.625], + [49.165, 5.151], + [49.455, 5.041], + [49.627, 3.955], + [50.048, 3.391], + [50.631, 3.282], + [50.546, 3.483], + [50.705, 3.573], + [51.14, 3.356], + [51.816, 3.319], + [51.952, 3.483], + [51.708, 3.773], + [52.034, 4.044], + [52.047, 4.244], + [51.673, 4.371], + [51.454, 4.699], + [51.042, 4.734], + [50.909, 4.936], + [51.914, 5.063], + [51.394, 5.259], + [51.841, 5.714], + [52.013, 5.643], + [52.254, 5.824], + [53.246, 5.897], + [53.658, 5.461], + [53.573, 5.696], + [53.826, 6.076], + [54.253, 6.203], + [53.913, 6.678], + [53.924, 6.875], + [54.132, 7.039], + [53.72, 7.477], + [53.767, 7.693], + [54.217, 7.711], + [54.179, 7.91], + [54.507, 8.401], + [54.99, 8.655], + [55.33, 8.618], + [55.537, 8.964], + [55.546, 9.636], + [56.08, 9.815], + [56.274, 9.706], + [56.784, 10.143], + [56.856, 10.47], + [57.184, 10.597], + [57.256, 10.995], + [57.401, 11.105], + [57.302, 11.358], + [57.473, 11.506], + [57.402, 12.012], + [57.619, 12.337], + [57.472, 12.775], + [57.642, 12.92], + [57.341, 13.011], + [57.414, 13.23], + [58.041, 13.411], + [58.189, 13.628], + [58.527, 13.591], + [58.734, 13.825], + [58.77, 14.171], + [59.11, 14.426], + [60.536, 14.897], + [60.514, 15.078], + [60.235, 15.241], + [60.429, 15.715], + [60.139, 16.039], + [60.49, 16.023], + [60.526, 16.477], + [60.805, 16.566], + [61.362, 17.512], + [61.617, 16.767], + [61.448, 16.005], + [61.506, 15.84], + [61.821, 15.733], + [62.234, 16.24], + [62.887, 16.204], + [63.034, 16.35], + [62.85, 16.04], + [62.899, 15.715], + [63.736, 15.042], + [64.654, 13.918], + [64.802, 13.953], + [64.716, 13.753], + [65.25, 13.246], + [65.191, 13.009], + [65.771, 13.083], + [66.256, 12.902], + [67.514, 11.994], + [67.478, 11.648], + [67.658, 11.704], + [67.72, 11.541], + [67.297, 11.378], + [67.091, 11.104], + [67.127, 10.977], + [67.443, 11.014], + [67.66, 10.797], + [68.168, 10.869], + [68.605, 10.651], + [69.125, 10.886], + [69.029, 11.032], + [69.16, 11.486], + [69.345, 11.32], + [69.113, 11.159], + [69.791, 11.051], + [70.082, 10.615], + [70.35, 10.65], + [70.568, 10.397], + [70.81, 10.541], + [71.366, 9.962], + [71.754, 9.78], + [71.813, 9.325], + [72.046, 9.054], + [72.299, 9.398], + [72.153, 9.725], + [72.215, 9.852], + [73.002, 10.054], + [72.324, 11.068], + [72.372, 11.212], + [72.237, 11.487], + [71.536, 12.103], + [71.973, 12.139], + [72.191, 12.464], + [72.625, 11.812], + [72.76, 11.83], + [72.771, 12.029], + [72.866, 11.704], + [73.377, 11.303], + [73.353, 11.158], + [74.151, 11.012], + [73.921, 11.468], + [74.335, 11.902], + [74.164, 12.266], + [74.637, 12.465], + [74.794, 12.81], + [74.768, 13.173], + [75.085, 13.283], + [75.182, 13.536], + [75.121, 14.134], + [75.691, 13.244], + [75.654, 12.502], + [75.98, 12.882], + [75.931, 13.282], + [76.03, 13.39], + [75.883, 13.463], + [75.883, 13.682], + [76.019, 13.954], + [76.477, 13.88], + [76.503, 13.664], + [76.732, 13.537], + [76.223, 13.101], + [76.527, 12.611], + [76.745, 12.611], + [76.889, 12.176], + [77.035, 12.139], + [77.178, 12.502], + [77.664, 12.794], + [77.471, 12.429], + [77.241, 12.338], + [77.156, 11.922], + [76.876, 11.795], + [76.889, 11.524], + [77.106, 11.63], + [77.398, 11.305], + [77.664, 11.45], + [77.954, 11.051], + [78.027, 11.305], + [78.221, 11.449], + [78.197, 11.27], + [78.393, 11.303], + [78.488, 11.521], + [79.02, 11.197], + [79.529, 11.233], + [79.677, 11.414], + [79.891, 11.267], + [79.639, 11.866], + [80.39, 11.904], + [80.413, 12.229], + [80.219, 12.483], + [80.391, 12.629], + [80.316, 12.776], + [80.595, 12.719], + [80.512, 12.955], + [80.801, 13.429], + [81.104, 13.517], + [81.177, 13.734], + [81.298, 13.664], + [81.408, 13.936], + [80.873, 14.225], + [81.031, 14.39], + [81.152, 14.316], + [81.407, 14.77], + [81.431, 15.061], + [81.624, 15.17], + [81.614, 15.37], + [81.977, 15.37], + [82.049, 15.769], + [82.399, 15.171], + [82.266, 14.86], + [81.938, 14.878], + [82.037, 14.444], + [81.623, 13.717], + [81.88, 13.591], + [82.341, 14.028], + [82.569, 13.936], + [82.304, 13.716], + [82.339, 13.373], + [82.026, 13.444], + [81.612, 13.227], + [81.275, 13.372], + [81.142, 13.174], + [81.291, 12.92], + [81.289, 12.856], + [80.885, 13.047], + [80.946, 12.665], + [81.19, 12.377], + [80.862, 12.395], + [80.754, 12.229], + [80.852, 11.832], + [80.523, 11.45], + [80.293, 11.467], + [80.631, 11.212], + [80.594, 11.016], + [80.389, 10.997], + [80.364, 10.778], + [81.008, 10.58], + [81.636, 9.998], + [81.745, 9.653], + [81.529, 9.509], + [81.54, 9.307], + [81.636, 9.416], + [82.316, 9.198], + [82.341, 9.417], + [83.019, 9.307], + [82.752, 9.054], + [82.399, 9.073], + [83.043, 8.873], + [83.211, 8.509], + [83.333, 8.908], + [83.575, 8.982], + [83.721, 8.799], + [84.084, 9.053], + [84.568, 8.655], + [84.908, 8.799], + [85.294, 8.619], + [85.876, 8.11], + [85.815, 8.527], + [85.67, 8.673], + [85.816, 8.78], + [85.741, 9.035], + [86.058, 9.508], + [85.343, 10.069], + [85.306, 10.487], + [85.041, 10.996], + [85.901, 12.285], + [87.961, 12.829], + [88.032, 12.43], + [88.178, 12.684], + [89.729, 12.646], + [91.933, 12.212], + [92.344, 12.031], + [92.78, 11.63], + [92.78, 11.486], + [93.046, 11.486], + [93.168, 11.266], + [93.459, 11.451], + [95.275, 11.666], + [95.517, 12.067], + [95.567, 11.595], + [95.737, 11.594], + [95.762, 11.741], + [96.026, 11.666], + [96.149, 11.92], + [96.292, 11.666], + [96.728, 11.884], + [96.838, 11.795], + [96.608, 11.521], + [96.85, 11.449], + [97.163, 11.666], + [97.238, 11.34], + [97.892, 11.087], + [99.539, 11.086], + [100, 11.521], + [99.733, 10.907], + [99.539, 10.939], + [99.224, 10.686], + [98.957, 10.76], + [98.39, 10.415], + [98.569, 9.889], + [99.078, 9.67], + [97.868, 10.068], + [97.625, 10.541], + [97.381, 10.579], + [97.323, 9.981], + [97.213, 9.852], + [95.711, 9.2], + [94.984, 7.275], + [94.307, 7.202], + [93.81, 6.712], + [93.737, 4.462], + [93.811, 4.135], + [93.665, 3.919], + [93.64, 3.338], + [93.411, 2.921], + [93.073, 2.738], + [92.756, 2.738], + [92.805, 2.919], + [92.501, 2.757], + [93.421, 2.03], + [93.714, 1.05], + [93.812, 0], + ], + meta: { + sourceLengthKm: 1971.7732460448683, + simplificationToleranceDeg: 0.0025, + minCellSizeWorld: 1.5625, + minCellSizeRealKm: 3.9936135910971187, + boundingBox: { + lonMin: -4.797583, + lonMax: -1.3575, + latMin: 47.203333, + latMax: 48.898278, + }, + }, +}; + +export const NORMANDIE: CoastRegion = { + name: "normandie", + worldSize: 100, + points: [ + [0, 33.811], + [0.494, 34.065], + [1.317, 34.172], + [1.436, 34.572], + [1.965, 34.735], + [1.505, 34.9], + [1.46, 35.19], + [2.119, 34.97], + [2.106, 34.589], + [2.26, 34.428], + [3.365, 34.356], + [5.509, 33.775], + [5.908, 33.776], + [5.884, 33.847], + [5.578, 33.847], + [5.4, 33.974], + [5.165, 34.446], + [5.25, 34.754], + [5.236, 34.446], + [5.507, 33.956], + [5.907, 33.883], + [6.425, 33.376], + [8.426, 32.576], + [10.543, 30.943], + [10.733, 31.09], + [10.78, 30.944], + [11.111, 31.523], + [10.923, 31.053], + [10.65, 30.817], + [11.227, 29.964], + [12.05, 29.238], + [13.346, 28.913], + [14.545, 28.296], + [14.924, 28.293], + [15.135, 28.585], + [15.499, 28.675], + [15.159, 28.511], + [15.016, 28.185], + [17.065, 28.077], + [18.688, 27.712], + [19.465, 28.004], + [18.984, 27.659], + [20.595, 27.024], + [22.183, 25.881], + [21.869, 25.861], + [20.125, 26.95], + [18.689, 27.242], + [14.97, 26.988], + [13.25, 26.699], + [12.898, 26.914], + [12.758, 26.622], + [11.722, 26.406], + [11.039, 25.898], + [11.015, 25.717], + [11.769, 26.263], + [12.687, 26.444], + [13.206, 26.297], + [13.358, 26.097], + [12.616, 26.334], + [12.064, 26.135], + [12.239, 26.043], + [11.535, 25.899], + [11.485, 25.789], + [11.735, 25.699], + [11.322, 25.717], + [10.923, 25.064], + [10.356, 24.775], + [10.299, 24.319], + [10.72, 23.013], + [12.745, 18.657], + [13.053, 16.878], + [14.263, 15.699], + [15.158, 15.483], + [16.335, 14.719], + [17.865, 14.321], + [18.432, 13.885], + [18.667, 13.486], + [18.784, 13.631], + [18.961, 13.613], + [18.761, 13.376], + [20.856, 12.361], + [22.101, 11.6], + [22.503, 11.163], + [23.139, 10.873], + [23.915, 10.181], + [24.832, 9.711], + [28.318, 8.768], + [28.434, 8.766], + [28.599, 9.168], + [28.528, 8.804], + [28.976, 8.913], + [30.459, 8.769], + [31.872, 8.078], + [34.649, 7.352], + [35.613, 6.807], + [37.403, 6.917], + [38.72, 6.445], + [39.097, 6.117], + [39.155, 6.354], + [39.062, 6.607], + [39.31, 6.843], + [39.182, 6.5], + [39.285, 6.154], + [42.37, 4.521], + [46.041, 1.236], + [47.077, 0.547], + [47.077, 0.4], + [47.314, 0.437], + [47.737, 0], + ], + meta: { + sourceLengthKm: 265.70253627275014, + simplificationToleranceDeg: 0.0025, + minCellSizeWorld: 1.5625, + minCellSizeRealKm: 3.9936135910971187, + boundingBox: { + lonMin: -0.299167, + lonMax: 1.390833, + latMin: 49.267056, + latMax: 50.075028, + }, + }, +}; + +export const COAST_REGIONS = { + bretagne: BRETAGNE, + normandie: NORMANDIE, +} as const; diff --git a/website/components/blog/box-dimension/shapes.ts b/website/components/blog/box-dimension/shapes.ts new file mode 100644 index 000000000..a0cbe3558 --- /dev/null +++ b/website/components/blog/box-dimension/shapes.ts @@ -0,0 +1,55 @@ +import type { Point } from "./boxCounting"; + +/** Shared world-unit coordinate box every shape/coastline is normalized into. */ +export const WORLD_SIZE = 100; + +export interface ShapeDefinition { + id: "line" | "circle" | "square"; + label: string; + points: Point[]; + mode: "line" | "filled"; + closed: boolean; +} + +function circlePoints(cx: number, cy: number, radius: number, segments: number): Point[] { + const points: Point[] = []; + for (let i = 0; i < segments; i++) { + const angle = (i / segments) * Math.PI * 2; + points.push({ x: cx + radius * Math.cos(angle), y: cy + radius * Math.sin(angle) }); + } + return points; +} + +export const LINE_SHAPE: ShapeDefinition = { + id: "line", + label: "Linie", + points: [ + { x: 10, y: 50 }, + { x: 90, y: 50 }, + ], + mode: "line", + closed: false, +}; + +export const CIRCLE_SHAPE: ShapeDefinition = { + id: "circle", + label: "Kreis", + points: circlePoints(50, 50, 40, 128), + mode: "line", + closed: true, +}; + +export const SQUARE_SHAPE: ShapeDefinition = { + id: "square", + label: "Volles Quadrat", + points: [ + { x: 10, y: 10 }, + { x: 90, y: 10 }, + { x: 90, y: 90 }, + { x: 10, y: 90 }, + ], + mode: "filled", + closed: true, +}; + +export const DEMO_SHAPES: ShapeDefinition[] = [LINE_SHAPE, CIRCLE_SHAPE, SQUARE_SHAPE]; diff --git a/website/package.json b/website/package.json index 9f937f9ef..3347ceacf 100644 --- a/website/package.json +++ b/website/package.json @@ -13,7 +13,8 @@ "test:watch": "vitest", "test:ui": "vitest --ui", "test:coverage": "vitest --coverage", - "send-webmentions": "tsx ./utils/sendWebmentions.ts" + "send-webmentions": "tsx ./utils/sendWebmentions.ts", + "build-coasts": "tsx ./utils/coastsToTs.ts" }, "dependencies": { "@fontsource-variable/source-code-pro": "^5.3.0", diff --git a/website/test/boxCounting.test.ts b/website/test/boxCounting.test.ts new file mode 100644 index 000000000..ade90c43b --- /dev/null +++ b/website/test/boxCounting.test.ts @@ -0,0 +1,78 @@ +import { describe, expect, it } from "vitest"; +import { + countLineCells, + countFilledCells, + parseCellKey, +} from "../components/blog/box-dimension/boxCounting"; +import type { Point } from "../components/blog/box-dimension/boxCounting"; + +function square(size: number): Point[] { + return [ + { x: 0, y: 0 }, + { x: size, y: 0 }, + { x: size, y: size }, + { x: 0, y: size }, + ]; +} + +describe("countLineCells", () => { + it("counts a horizontal line as ceil(length / cellSize) cells", () => { + const line: Point[] = [ + { x: 0, y: 5 }, + { x: 97, y: 5 }, + ]; + expect(countLineCells(line, 10).count).toBe(10); // ceil(97/10) + }); + + it("counts a vertical line the same way", () => { + const line: Point[] = [ + { x: 5, y: 0 }, + { x: 5, y: 33 }, + ]; + expect(countLineCells(line, 10).count).toBe(4); // ceil(33/10) + }); + + it("doubles the count when cell size halves, for a straight line", () => { + const line: Point[] = [ + { x: 0, y: 5 }, + { x: 100, y: 5 }, + ]; + const coarse = countLineCells(line, 10).count; + const fine = countLineCells(line, 5).count; + expect(fine).toBe(coarse * 2); + }); + + it("returns empty for an empty point list", () => { + expect(countLineCells([], 10)).toEqual({ count: 0, cells: [] }); + }); + + it("handles a single point", () => { + const result = countLineCells([{ x: 12, y: 34 }], 10); + expect(result.count).toBe(1); + expect(parseCellKey(result.cells[0])).toEqual([1, 3]); + }); + + it("closes the loop when closed=true", () => { + const openCount = countLineCells(square(40), 10, false).count; + const closedCount = countLineCells(square(40), 10, true).count; + expect(closedCount).toBeGreaterThan(openCount); + }); +}); + +describe("countFilledCells", () => { + it("counts a 40x40 square at cellSize=10 as exactly 16 cells", () => { + expect(countFilledCells(square(40), 10).count).toBe(16); + }); + + it("quadruples when cell size halves — the dimension-2 signature", () => { + const coarse = countFilledCells(square(40), 10).count; + const fine = countFilledCells(square(40), 5).count; + expect(coarse).toBe(16); + expect(fine).toBe(64); + expect(fine).toBe(coarse * 4); + }); + + it("returns empty for a degenerate polygon", () => { + expect(countFilledCells([{ x: 0, y: 0 }], 10)).toEqual({ count: 0, cells: [] }); + }); +}); diff --git a/website/test/dimensionSkala.test.ts b/website/test/dimensionSkala.test.ts new file mode 100644 index 000000000..3852e2804 --- /dev/null +++ b/website/test/dimensionSkala.test.ts @@ -0,0 +1,75 @@ +import { describe, expect, it } from "vitest"; +import { factorToDimension, aggregateFactor } from "../components/blog/box-dimension/DimensionSkala"; + +describe("factorToDimension", () => { + it("maps factor 2 (a curve) to dimension 1", () => { + expect(factorToDimension(2)).toBeCloseTo(1, 10); + }); + + it("maps factor 4 (a filled area) to dimension 2", () => { + expect(factorToDimension(4)).toBeCloseTo(2, 10); + }); + + it("maps factor 1 to dimension 1 (clamped)", () => { + expect(factorToDimension(1)).toBeCloseTo(1, 10); + }); + + it("clamps factors below 1", () => { + expect(factorToDimension(0.5)).toBeCloseTo(1, 10); + }); + + it("clamps factors above 4", () => { + expect(factorToDimension(10)).toBeCloseTo(2, 10); + }); + + it("is monotonically increasing between 1 and 4", () => { + expect(factorToDimension(2)).toBeLessThan(factorToDimension(2.5)); + expect(factorToDimension(2.5)).toBeLessThan(factorToDimension(3)); + }); +}); + +describe("aggregateFactor", () => { + it("returns null with fewer than 2 samples", () => { + expect(aggregateFactor([])).toBeNull(); + expect(aggregateFactor([{ cellSize: 10, count: 4 }])).toBeNull(); + }); + + it("recovers a clean 2x-per-halving factor across 3 steps", () => { + const samples = [ + { cellSize: 40, count: 4 }, + { cellSize: 20, count: 8 }, + { cellSize: 10, count: 16 }, + ]; + expect(aggregateFactor(samples)).toBeCloseTo(2, 10); + }); + + it("recovers a clean 4x-per-halving factor (filled area)", () => { + const samples = [ + { cellSize: 40, count: 4 }, + { cellSize: 20, count: 16 }, + { cellSize: 10, count: 64 }, + ]; + expect(aggregateFactor(samples)).toBeCloseTo(4, 10); + }); + + it("ignores sample order", () => { + const forward = aggregateFactor([ + { cellSize: 40, count: 4 }, + { cellSize: 10, count: 16 }, + ]); + const reversed = aggregateFactor([ + { cellSize: 10, count: 16 }, + { cellSize: 40, count: 4 }, + ]); + expect(forward).toBeCloseTo(reversed!, 10); + }); + + it("returns null when all samples share the same cell size", () => { + expect( + aggregateFactor([ + { cellSize: 10, count: 4 }, + { cellSize: 10, count: 4 }, + ]), + ).toBeNull(); + }); +}); diff --git a/website/utils/coastsToTs.ts b/website/utils/coastsToTs.ts new file mode 100644 index 000000000..ae2152c9d --- /dev/null +++ b/website/utils/coastsToTs.ts @@ -0,0 +1,92 @@ +#!/usr/bin/env tsx +/** + * Converts the JSON produced by `notebooks/box_dimension_coastlines.py` into the + * committed `coasts.ts` module the box-dimension blog widgets import. + * + * All the actual geo-processing (downloading GSHHG, clipping, simplifying, + * projecting) lives in the Python script — this step only reshapes its JSON + * output into a typed, generated TypeScript file, so re-running the whole + * pipeline after a tolerance/bbox tweak is two commands: + * + * cd notebooks && uv run python box_dimension_coastlines.py + * cd website && npm run build-coasts + */ + +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const INPUT_PATH = path.resolve(__dirname, "../../notebooks/output/box_dimension_coasts.json"); +const OUTPUT_PATH = path.resolve(__dirname, "../components/blog/box-dimension/coasts.ts"); + +interface RawRegion { + name: string; + points: [number, number][]; + worldSize: number; + meta: { + sourceLengthKm: number; + simplificationToleranceDeg: number; + minCellSizeWorld: number; + minCellSizeRealKm: number; + boundingBox: { lonMin: number; lonMax: number; latMin: number; latMax: number }; + }; +} + +function regionConst(id: string, region: RawRegion): string { + const constName = id.toUpperCase(); + return `export const ${constName}: CoastRegion = { + name: ${JSON.stringify(region.name)}, + worldSize: ${region.worldSize}, + points: ${JSON.stringify(region.points)}, + meta: ${JSON.stringify(region.meta, null, 2).replace(/\n/g, "\n ")}, +}; +`; +} + +function main() { + if (!fs.existsSync(INPUT_PATH)) { + console.error(`Missing ${INPUT_PATH}.`); + console.error("Run: cd notebooks && uv run python box_dimension_coastlines.py"); + process.exit(1); + } + + const raw = JSON.parse(fs.readFileSync(INPUT_PATH, "utf-8")) as Record; + const ids = Object.keys(raw); + + const header = `// GENERATED FILE — do not hand-edit. +// Source: notebooks/output/box_dimension_coasts.json +// Regenerate: cd notebooks && uv run python box_dimension_coastlines.py +// cd website && npm run build-coasts + +export interface CoastRegion { + name: string; + worldSize: number; + points: [number, number][]; + meta: { + sourceLengthKm: number; + simplificationToleranceDeg: number; + minCellSizeWorld: number; + minCellSizeRealKm: number; + boundingBox: { lonMin: number; lonMax: number; latMin: number; latMax: number }; + }; +} + +`; + + const constants = ids.map((id) => regionConst(id, raw[id])).join("\n"); + + const registry = `export const COAST_REGIONS = { +${ids.map((id) => ` ${id}: ${id.toUpperCase()},`).join("\n")} +} as const; +`; + + fs.writeFileSync(OUTPUT_PATH, header + constants + "\n" + registry); + + const bytes = fs.statSync(OUTPUT_PATH).size; + console.log(`Wrote ${OUTPUT_PATH} (${(bytes / 1024).toFixed(1)} KB)`); +} + +main(); From f04cbd12f0474e435cbdf372bd68ab01748156f8 Mon Sep 17 00:00:00 2001 From: fretchen Date: Sun, 23 Aug 2026 19:01:58 +0200 Subject: [PATCH 04/12] Cleaner Widget --- website/blog/kuesten_dimension.mdx | 16 ++-- .../blog/box-dimension/DimensionSkala.tsx | 91 ------------------- .../blog/box-dimension/KaestchenSpiel.tsx | 6 +- .../blog/box-dimension/KuestenSpiel.tsx | 12 +-- .../blog/box-dimension/Messreihe.tsx | 77 +++++++++++----- website/test/dimensionSkala.test.ts | 75 --------------- 6 files changed, 66 insertions(+), 211 deletions(-) delete mode 100644 website/components/blog/box-dimension/DimensionSkala.tsx delete mode 100644 website/test/dimensionSkala.test.ts diff --git a/website/blog/kuesten_dimension.mdx b/website/blog/kuesten_dimension.mdx index 7929fde39..d79a48aee 100644 --- a/website/blog/kuesten_dimension.mdx +++ b/website/blog/kuesten_dimension.mdx @@ -23,7 +23,7 @@ Bevor du weiterliest: Was schätzt du — bei einer **geraden Linie**, wie viel Probier es im Widget unten aus, an drei Formen: einer geraden Linie, einem Kreis und einem voll ausgemalten Quadrat. - + Das Ergebnis: Bei der Linie und beim Kreis brauchst du **doppelt** so viele Kästchen, egal wie oft du halbierst. Beim Quadrat brauchst du **viermal** so viele. @@ -37,25 +37,25 @@ Jetzt kommt der Clou. Was passiert, wenn du dasselbe Spiel nicht mit einer Linie -Schau dir den Faktor an, den das Widget für die Bretagne anzeigt. Er ist **nicht 2** und **nicht 4**. Er liegt irgendwo dazwischen. +Schau dir die ×-Zahlen in deiner Messreihe an. Sie sind **nicht ×2** und **nicht ×4**. Sie liegen irgendwo dazwischen. -Das bedeutet: Die Küste ist mehr als eine Linie, aber weniger als eine volle Fläche. Ihre Dimension liegt zwischen 1 und 2 — das Widget zeigt für die Bretagne ungefähr 1,3. Eine Zahl, die keine ganze Zahl ist, obwohl "Dimension" für dich bisher wahrscheinlich immer 1, 2 oder 3 war. +Das bedeutet: Die Küste ist mehr als eine Linie, aber weniger als eine volle Fläche. Genau das siehst du an den ×-Zahlen, die näher an ×4 als an ×2 liegen. Ihre Dimension liegt deshalb zwischen 1 und 2 — eine Zahl, die keine ganze Zahl ist, obwohl "Dimension" für dich bisher wahrscheinlich immer 1, 2 oder 3 war. Formen, deren Dimension zwischen den ganzen Zahlen liegt, nennt man **Fraktale**. Eine Küste ist ein Fraktal, weil sie in jedem Maßstab neue kleine Zacken zeigt — zoomst du rein, taucht die nächste Miniaturbucht auf, und die nächste, und die nächste. -Schalte jetzt im Widget auf die Küste der Normandie um. Der Faktor sinkt deutlich, auf eine Dimension von ungefähr 1,1. Diese Küste sieht auf der Karte ganz anders aus als die Bretagne: viel glatter, viel gerader, mit weniger kleinen Einbuchtungen. Und genau das zeigt sich auch im gemessenen Faktor — eine glattere Küste liegt näher an der Dimension 1, eine zerklüftete Küste näher an der Dimension 2. +Schalte jetzt im Widget auf die Küste der Normandie um. Die ×-Zahlen in der Messreihe werden kleiner, näher an ×2. Diese Küste sieht auf der Karte ganz anders aus als die Bretagne: viel glatter, viel gerader, mit weniger kleinen Einbuchtungen. Und genau das zeigt sich auch in den ×-Zahlen — eine glattere Küste liegt näher an ×2 (Dimension 1), eine zerklüftete Küste näher an ×4 (Dimension 2). Der Mathematiker Benoît Mandelbrot hat genau das 1967 zum ersten Mal systematisch gemessen, an der Westküste Großbritanniens. Er kam auf eine Dimension von etwa 1,25 — eine Zahl, die bis heute in jedem Buch über Fraktale steht. ## Selber malen -Der beste Weg, ein Gefühl dafür zu bekommen, ist: selbst ausprobieren. Tipp im Widget oben auf "✏️ Selber malen" — oder hier, in einer frischen Runde: +Der beste Weg, ein Gefühl dafür zu bekommen, ist: selbst ausprobieren. Hier ist ein leeres Feld — mal drauflos: - + -Mal mit dem Finger eine ruhige, geschwungene Linie. Schau dir ihre Dimension an — sie sollte nah bei 1 liegen. Mal jetzt eine wilde, zackige Kritzellinie, so wild du kannst. Die Dimension steigt. +Mal mit dem Finger eine ruhige, geschwungene Linie. Schau dir die ×-Zahlen in deiner Messreihe an — sie sollten nah bei ×2 liegen. Mal jetzt eine wilde, zackige Kritzellinie, so wild du kannst. Die Zahlen werden größer. -Du hast damit selbst eine Küste gebaut, nur eben mit dem Finger statt mit dem Meer. Und du hast gemessen, wie "wild" sie ist — mit genau derselben Zahl, die auch für die Bretagne, für Großbritannien und für jede andere Küste der Erde funktioniert. +Du hast damit selbst eine Küste gebaut, nur eben mit dem Finger statt mit dem Meer. Und du hast gemessen, wie "wild" sie ist — mit genau demselben Trick, der auch für die Bretagne, für Großbritannien und für jede andere Küste der Erde funktioniert. diff --git a/website/components/blog/box-dimension/DimensionSkala.tsx b/website/components/blog/box-dimension/DimensionSkala.tsx deleted file mode 100644 index 3f85b24fd..000000000 --- a/website/components/blog/box-dimension/DimensionSkala.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import React from "react"; -import { css } from "../../../styled-system/css"; -import { ESSAY_ACCENT } from "../palette"; - -/** - * Maps a halving-factor (how many times more cells you need when you halve the - * cell size — 2x for a curve, 4x for a filled area) onto a dimension between 1 - * and 2. This is exactly log2(factor) + 1, but that never appears in the UI — - * the widgets only ever show the gauge position, never the exponent. - */ -export function factorToDimension(factor: number): number { - const clamped = Math.max(2, Math.min(factor, 4)); - return Math.log2(clamped); -} - -export interface HalvingSample { - cellSize: number; - count: number; -} - -/** - * Combines two or more halving steps into one stabilized factor, via the - * geometric mean per step between the coarsest and finest sample. Ratios - * telescope, so this is the same thing as an average log-log slope — computed - * without ever naming a logarithm. - */ -export function aggregateFactor(samples: HalvingSample[]): number | null { - if (samples.length < 2) return null; - - const sorted = [...samples].sort((a, b) => b.cellSize - a.cellSize); - const coarsest = sorted[0]; - const finest = sorted[sorted.length - 1]; - - if (coarsest.count <= 0 || finest.count <= 0) return null; - - const numHalvings = Math.log2(coarsest.cellSize / finest.cellSize); - if (!isFinite(numHalvings) || numHalvings <= 0) return null; - - return Math.pow(finest.count / coarsest.count, 1 / numHalvings); -} - -const wrapper = css({ mt: "3" }); - -const track = css({ - position: "relative", - height: "8px", - bg: "gray.200", - borderRadius: "full", -}); - -const marker = css({ - position: "absolute", - top: "-4px", - width: "4px", - height: "16px", - borderRadius: "sm", -}); - -const labels = css({ - display: "flex", - justifyContent: "space-between", - fontSize: "xs", - color: "gray.600", - mt: "1", -}); - -export interface DimensionSkalaProps { - /** Measured halving-factor, or null before enough samples exist. */ - factor: number | null; -} - -export function DimensionSkala({ factor }: DimensionSkalaProps) { - const dimension = factor !== null ? factorToDimension(factor) : null; - const percent = dimension !== null ? Math.max(0, Math.min(1, dimension - 1)) * 100 : null; - - return ( -
-
- {percent !== null && ( -
- )} -
-
- Linie (1) - Fläche (2) -
-
- ); -} - -export default DimensionSkala; diff --git a/website/components/blog/box-dimension/KaestchenSpiel.tsx b/website/components/blog/box-dimension/KaestchenSpiel.tsx index a172c6066..faf87b320 100644 --- a/website/components/blog/box-dimension/KaestchenSpiel.tsx +++ b/website/components/blog/box-dimension/KaestchenSpiel.tsx @@ -1,8 +1,7 @@ import React, { useState } from "react"; import { css } from "../../../styled-system/css"; import { BoxCanvas } from "./BoxCanvas"; -import { DimensionSkala, aggregateFactor, type HalvingSample } from "./DimensionSkala"; -import { Messreihe } from "./Messreihe"; +import { Messreihe, type HalvingSample } from "./Messreihe"; import { countLineCells, countFilledCells, type Point } from "./boxCounting"; import { DEMO_SHAPES, WORLD_SIZE, type ShapeDefinition } from "./shapes"; @@ -109,7 +108,6 @@ export function KaestchenSpiel({ variant = "shapes" }: KaestchenSpielProps) { setRevealed(false); } - const overallFactor = aggregateFactor(samples); const isLastStep = stepIndex === CELL_SIZE_STEPS.length - 1; return ( @@ -167,8 +165,6 @@ export function KaestchenSpiel({ variant = "shapes" }: KaestchenSpielProps) { {revealed &&

{currentCount} Kästchen

} - - {overallFactor !== null && } )}
diff --git a/website/components/blog/box-dimension/KuestenSpiel.tsx b/website/components/blog/box-dimension/KuestenSpiel.tsx index 15c81b050..a75f396e7 100644 --- a/website/components/blog/box-dimension/KuestenSpiel.tsx +++ b/website/components/blog/box-dimension/KuestenSpiel.tsx @@ -1,8 +1,7 @@ import React, { useState } from "react"; import { css } from "../../../styled-system/css"; import { BoxCanvas } from "./BoxCanvas"; -import { DimensionSkala, aggregateFactor, type HalvingSample } from "./DimensionSkala"; -import { Messreihe } from "./Messreihe"; +import { Messreihe, type HalvingSample } from "./Messreihe"; import { countLineCells, type Point } from "./boxCounting"; import { COAST_REGIONS } from "./coasts"; @@ -83,7 +82,6 @@ export function KuestenSpiel() { setRevealed(false); } - const overallFactor = aggregateFactor(samples); const isLastStep = stepIndex === CELL_SIZE_STEPS.length - 1; return ( @@ -125,13 +123,7 @@ export function KuestenSpiel() { {revealed &&

{currentCount} Kästchen

} - {immediateFactor !== null && ( -

- Das ist das {immediateFactor.toFixed(1)}-fache von vorhin, nachdem die Kästchen halbiert wurden. -

- )} - - {overallFactor !== null && } +
); } diff --git a/website/components/blog/box-dimension/Messreihe.tsx b/website/components/blog/box-dimension/Messreihe.tsx index d66aea1c4..ddbb163bd 100644 --- a/website/components/blog/box-dimension/Messreihe.tsx +++ b/website/components/blog/box-dimension/Messreihe.tsx @@ -1,12 +1,28 @@ import React from "react"; import { css } from "../../../styled-system/css"; import { ESSAY_ACCENT } from "../palette"; -import type { HalvingSample } from "./DimensionSkala"; + +export interface HalvingSample { + cellSize: number; + count: number; +} const wrapper = css({ mt: "3" }); const caption = css({ fontSize: "sm", color: "gray.600", mb: "1" }); +const headerRow = css({ + display: "flex", + alignItems: "center", + gap: "2", + fontSize: "xs", + color: "gray.500", + mb: "1", +}); + +const headerSize = css({ width: "40px", flexShrink: 0 }); +const headerCount = css({ flex: "1" }); + const row = css({ display: "flex", alignItems: "center", @@ -14,12 +30,16 @@ const row = css({ mt: "1", }); -const rowLabel = css({ - fontSize: "sm", - color: "gray.700", - whiteSpace: "nowrap", - width: "90px", +const sizeCell = css({ + width: "40px", flexShrink: 0, + display: "flex", + justifyContent: "center", +}); + +const swatch = css({ + border: "2px solid token(colors.explore, #7c3aed)", + borderRadius: "2px", }); const barTrack = css({ @@ -35,28 +55,29 @@ const barFill = css({ borderRadius: "sm", }); -const rowCount = css({ +const countCell = css({ fontSize: "sm", - fontWeight: "bold", - color: "explore", - width: "36px", + width: "80px", textAlign: "right", flexShrink: 0, }); -export interface MessreiheProps { - /** Halving steps revealed so far, in any order — rendered coarsest-first. */ - samples: HalvingSample[]; - worldSize: number; +const countNumber = css({ fontWeight: "bold", color: "explore" }); + +const factorTag = css({ color: "gray.500", fontSize: "xs", ml: "1" }); + +function formatFactor(factor: number): string { + return factor.toLocaleString("de-DE", { minimumFractionDigits: 1, maximumFractionDigits: 1 }); } /** - * Renders the halving steps taken so far as growing bars instead of a computed - * ratio sentence. "Kästchen pro Kante" (worldSize / cellSize) is always a clean - * integer for the widgets' own CELL_SIZE_STEPS, so no world-unit decimal ever - * reaches the child — they compare two whole numbers per row themselves. + * Renders the halving steps taken so far as growing bars, each with a shrinking + * square icon standing in for "wie klein die Kästchen gerade sind" — no raw + * cellSize or "N pro Kante" number is ever shown, since neither means anything + * to a child without translation. The ×-factor next to each count does that one + * division for them so the growth is visible without mental arithmetic. */ -export function Messreihe({ samples, worldSize }: MessreiheProps) { +export function Messreihe({ samples, worldSize }: { samples: HalvingSample[]; worldSize: number }) { if (samples.length === 0) return null; const rows = [...samples].sort((a, b) => b.cellSize - a.cellSize); @@ -65,16 +86,28 @@ export function Messreihe({ samples, worldSize }: MessreiheProps) { return (

Deine Messungen:

- {rows.map((r) => { +
+ Größe der Kästchen + Anzahl der Kästchen +
+ {rows.map((r, i) => { const perEdge = Math.round(worldSize / r.cellSize); + const sizePx = Math.max(6, Math.round(28 / Math.sqrt(perEdge / 4))); const percent = maxCount > 0 ? (r.count / maxCount) * 100 : 0; + const factor = i > 0 ? r.count / rows[i - 1].count : null; + return (
- {perEdge} pro Kante +
+ +
- {r.count} + + {r.count} + {factor !== null && ×{formatFactor(factor)}} +
); })} diff --git a/website/test/dimensionSkala.test.ts b/website/test/dimensionSkala.test.ts deleted file mode 100644 index 3852e2804..000000000 --- a/website/test/dimensionSkala.test.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { factorToDimension, aggregateFactor } from "../components/blog/box-dimension/DimensionSkala"; - -describe("factorToDimension", () => { - it("maps factor 2 (a curve) to dimension 1", () => { - expect(factorToDimension(2)).toBeCloseTo(1, 10); - }); - - it("maps factor 4 (a filled area) to dimension 2", () => { - expect(factorToDimension(4)).toBeCloseTo(2, 10); - }); - - it("maps factor 1 to dimension 1 (clamped)", () => { - expect(factorToDimension(1)).toBeCloseTo(1, 10); - }); - - it("clamps factors below 1", () => { - expect(factorToDimension(0.5)).toBeCloseTo(1, 10); - }); - - it("clamps factors above 4", () => { - expect(factorToDimension(10)).toBeCloseTo(2, 10); - }); - - it("is monotonically increasing between 1 and 4", () => { - expect(factorToDimension(2)).toBeLessThan(factorToDimension(2.5)); - expect(factorToDimension(2.5)).toBeLessThan(factorToDimension(3)); - }); -}); - -describe("aggregateFactor", () => { - it("returns null with fewer than 2 samples", () => { - expect(aggregateFactor([])).toBeNull(); - expect(aggregateFactor([{ cellSize: 10, count: 4 }])).toBeNull(); - }); - - it("recovers a clean 2x-per-halving factor across 3 steps", () => { - const samples = [ - { cellSize: 40, count: 4 }, - { cellSize: 20, count: 8 }, - { cellSize: 10, count: 16 }, - ]; - expect(aggregateFactor(samples)).toBeCloseTo(2, 10); - }); - - it("recovers a clean 4x-per-halving factor (filled area)", () => { - const samples = [ - { cellSize: 40, count: 4 }, - { cellSize: 20, count: 16 }, - { cellSize: 10, count: 64 }, - ]; - expect(aggregateFactor(samples)).toBeCloseTo(4, 10); - }); - - it("ignores sample order", () => { - const forward = aggregateFactor([ - { cellSize: 40, count: 4 }, - { cellSize: 10, count: 16 }, - ]); - const reversed = aggregateFactor([ - { cellSize: 10, count: 16 }, - { cellSize: 40, count: 4 }, - ]); - expect(forward).toBeCloseTo(reversed!, 10); - }); - - it("returns null when all samples share the same cell size", () => { - expect( - aggregateFactor([ - { cellSize: 10, count: 4 }, - { cellSize: 10, count: 4 }, - ]), - ).toBeNull(); - }); -}); From f047b80814421a30254a900c1fe833f6d7cbc994 Mon Sep 17 00:00:00 2001 From: fretchen Date: Sun, 23 Aug 2026 22:10:03 +0200 Subject: [PATCH 05/12] Cleaning up the post etc. --- notebooks/box_dimension_coastlines.py | 149 +- notebooks/output/box_dimension_coasts.json | 7054 ++++++++++++----- website/MDX_MIGRATION.md | 298 + website/blog/kuesten_dimension.mdx | 2 +- .../blog/box-dimension/BoxCanvas.tsx | 25 +- .../blog/box-dimension/KaestchenSpiel.tsx | 55 +- .../blog/box-dimension/KuestenSpiel.tsx | 78 +- .../blog/box-dimension/Messreihe.tsx | 64 +- .../components/blog/box-dimension/coasts.ts | 1896 +---- .../components/blog/box-dimension/shapes.ts | 26 +- website/utils/coastsToTs.ts | 3 + 11 files changed, 5523 insertions(+), 4127 deletions(-) create mode 100644 website/MDX_MIGRATION.md diff --git a/notebooks/box_dimension_coastlines.py b/notebooks/box_dimension_coastlines.py index eceadde58..39065a002 100644 --- a/notebooks/box_dimension_coastlines.py +++ b/notebooks/box_dimension_coastlines.py @@ -63,10 +63,14 @@ def to_shapely(self) -> "box": # Bounding boxes as pinned by the approved blog post plan (kuesten_dimension.plan.md). REGIONS: dict[str, BBox] = { "bretagne": BBox(lon_min=-5.2, lon_max=-1.0, lat_min=47.2, lat_max=48.9), - # Ouistreham to Le Tréport, deliberately excluding the Cotentin peninsula - # (Cap de la Hague) — that stretch is itself jagged and would muddy the - # "smooth contrast coast" comparison the post makes against Bretagne. - "normandie": BBox(lon_min=-0.3, lon_max=1.4, lat_min=49.2, lat_max=50.1), + # Calvados D-Day beaches / Baie des Veys to Le Tréport. Deliberately stops + # short of the Cotentin peninsula proper (Cherbourg / Cap de la Hague) — + # that stretch is itself jagged and would muddy the "smooth contrast + # coast" comparison the post makes against Bretagne. Also deliberately + # doesn't extend past Le Tréport, which sits almost exactly on the + # Normandy/Picardy département border — going further east would mean + # showing non-Normandy coast under the "Normandie" label. + "normandie": BBox(lon_min=-1.3, lon_max=1.4, lat_min=49.1, lat_max=50.1), } # Douglas-Peucker tolerance in degrees. Must stay well below the real-world @@ -78,6 +82,17 @@ def to_shapely(self) -> "box": # plan for the check that this doesn't erase the jaggedness being measured. SIMPLIFY_TOLERANCE_DEG = 0.0025 +# Separate, coarser tolerance for the land/water fill polygon — it only needs +# to look right at widget scale, not defend a box-counting measurement, so it +# can be simplified much more aggressively to keep the payload small. +LAND_SIMPLIFY_TOLERANCE_DEG = 0.01 + +# Minimum land fragment area to keep, in km². A bbox like Bretagne's picks up +# hundreds of tiny offshore rocks/islets (GSHHG resolves them individually) — +# below this size they're sub-pixel at the widget's canvas scale anyway, so +# they're dropped rather than bloating the payload with invisible detail. +MIN_LAND_FRAGMENT_KM2 = 1.0 + def download_gshhg() -> Path: shapefile_path = DATA_DIR / LEVEL1_SHAPEFILE @@ -189,8 +204,35 @@ def on_frame(pt: tuple[float, float]) -> bool: KM_PER_DEG = 111.32 # rough, fine at these latitudes -def project_to_km(line: LineString) -> tuple[list[tuple[float, float]], dict]: - """Equirectangular projection into real kilometres (not yet fit to WORLD_SIZE). +def extract_land_polygons(polygons: list[Polygon], bbox: BBox) -> list[Polygon]: + """The clipped land area itself, for the water/land fill — unlike + extract_coastline, the bbox-frame edges are kept: they're the true edge of + the visible map here, not a measurement artifact. + + Tiny fragments (see MIN_LAND_FRAGMENT_KM2) are dropped: a bbox like + Bretagne's overlaps hundreds of individually-resolved offshore rocks that + are sub-pixel at the widget's canvas scale and would only bloat the payload. + """ + target = bbox.to_shapely() + merged = unary_union(polygons) + clipped = merged.intersection(target) + geoms = list(clipped.geoms) if hasattr(clipped, "geoms") else [clipped] + + lat_mean = (bbox.lat_min + bbox.lat_max) / 2 + km2_per_deg2 = KM_PER_DEG * KM_PER_DEG * math.cos(math.radians(lat_mean)) + + return [ + g + for g in geoms + if isinstance(g, Polygon) + and not g.is_empty + and g.area * km2_per_deg2 >= MIN_LAND_FRAGMENT_KM2 + ] + + +def make_projector(bbox: BBox): + """Equirectangular projection into real kilometres (not yet fit to WORLD_SIZE), + shared by every geometry (coastline *and* land polygons) of one region. Longitude is scaled by cos(mean latitude) so x/y are both in true km — a single shared scale (computed later, across all regions) then converts km @@ -198,35 +240,44 @@ def project_to_km(line: LineString) -> tuple[list[tuple[float, float]], dict]: if each region picked its own scale to fill the world box edge-to-edge, the same slider cellSize would silently mean a different real-world distance for each region. + + The reference point is the bbox corner, not a coordinate taken from + whichever geometry happens to be projected first — using two different + geometries' own coordinates as their reference would silently offset them + from each other, and the coastline and its land fill must stay aligned. """ - coords = list(line.coords) - lats = [c[1] for c in coords] - lons = [c[0] for c in coords] - lat_mean = sum(lats) / len(lats) + lat_mean = (bbox.lat_min + bbox.lat_max) / 2 cos_lat = math.cos(math.radians(lat_mean)) + lon0, lat0 = bbox.lon_min, bbox.lat_min - lon0, lat0 = lons[0], lats[0] - km_coords = [ - ((lon - lon0) * cos_lat * KM_PER_DEG, -(lat - lat0) * KM_PER_DEG) - for lon, lat in zip(lons, lats) - ] + def project(lon: float, lat: float) -> tuple[float, float]: + return ((lon - lon0) * cos_lat * KM_PER_DEG, -(lat - lat0) * KM_PER_DEG) + + return project - bbox_meta = { - "lonMin": min(lons), - "lonMax": max(lons), - "latMin": min(lats), - "latMax": max(lats), - } - return km_coords, bbox_meta + +def project_coords_to_km(coords: list[tuple[float, float]], project) -> list[tuple[float, float]]: + return [project(lon, lat) for lon, lat in coords] def normalize_with_shared_scale( - km_coords: list[tuple[float, float]], scale: float + km_coords: list[tuple[float, float]], scale: float, offset_x: float = 0, offset_y: float = 0 ) -> list[list[float]]: - xs = [p[0] for p in km_coords] - ys = [p[1] for p in km_coords] - x_min, y_min = min(xs), min(ys) - return [[round((x - x_min) * scale, 3), round((y - y_min) * scale, 3)] for x, y in km_coords] + return [[round(x * scale + offset_x, 3), round(y * scale + offset_y, 3)] for x, y in km_coords] + + +def center_in_world(points: list[list[float]], world_size: float) -> tuple[float, float]: + """Returns the (offset_x, offset_y) that centers `points` (already scaled to + world units, anchored at the origin) within the world_size x world_size box. + + Both regions share one scale (see main()), so a physically smaller region + like Normandie doesn't fill the box and would otherwise sit jammed in a + corner instead of looking like a normal, centered map. + """ + xs = [p[0] for p in points] + ys = [p[1] for p in points] + width, height = max(xs) - min(xs), max(ys) - min(ys) + return (world_size - width) / 2 - min(xs), (world_size - height) / 2 - min(ys) def _haversine_length_km(coords: list[tuple[float, float]]) -> float: @@ -263,13 +314,31 @@ def process_region(name: str, bbox: BBox, shapefile_path: Path) -> dict: f"{TARGET_POINT_BUDGET} budget — consider a coarser SIMPLIFY_TOLERANCE_DEG." ) - km_coords, bbox_meta = project_to_km(simplified) + project = make_projector(bbox) + + line_coords = list(simplified.coords) + lons = [c[0] for c in line_coords] + lats = [c[1] for c in line_coords] + bbox_meta = {"lonMin": min(lons), "lonMax": max(lons), "latMin": min(lats), "latMax": max(lats)} + + km_coords = project_coords_to_km(line_coords, project) x_range = max(x for x, _ in km_coords) - min(x for x, _ in km_coords) y_range = max(y for _, y in km_coords) - min(y for _, y in km_coords) + land_polygons = extract_land_polygons(polygons, bbox) + land_rings_km = [] + for poly in land_polygons: + simplified_ring = poly.exterior.simplify( + LAND_SIMPLIFY_TOLERANCE_DEG, preserve_topology=True + ) + land_rings_km.append(project_coords_to_km(list(simplified_ring.coords), project)) + ring_points = sum(len(r) for r in land_rings_km) + print(f" land fill: {len(land_rings_km)} ring(s), {ring_points} points total") + return { "name": name, "km_coords": km_coords, + "land_rings_km": land_rings_km, "sourceLengthKm": simplified_km, "boundingBox": bbox_meta, "spanKm": max(x_range, y_range), @@ -303,7 +372,16 @@ def main(): regions_out = {} for name, raw in regions_raw.items(): - points = normalize_with_shared_scale(raw["km_coords"], shared_scale) + # Center in the shared world box first (based on the coastline's own extent), + # then apply the exact same scale+offset to the land rings so both stay aligned. + unscaled_points = normalize_with_shared_scale(raw["km_coords"], shared_scale) + offset_x, offset_y = center_in_world(unscaled_points, WORLD_SIZE) + points = normalize_with_shared_scale(raw["km_coords"], shared_scale, offset_x, offset_y) + land_rings = [ + normalize_with_shared_scale(ring, shared_scale, offset_x, offset_y) + for ring in raw["land_rings_km"] + ] + meta = { "sourceLengthKm": raw["sourceLengthKm"], "simplificationToleranceDeg": SIMPLIFY_TOLERANCE_DEG, @@ -311,12 +389,19 @@ def main(): "minCellSizeRealKm": min_cell_size_real_km, "boundingBox": raw["boundingBox"], } - approx_bytes = len(json.dumps(points)) + approx_bytes = len(json.dumps(points)) + len(json.dumps(land_rings)) print( - f" {name}: {len(points)} points, ~{approx_bytes / 1024:.1f} KB, span used " + f" {name}: {len(points)} line points + {sum(len(r) for r in land_rings)} land " + f"points, ~{approx_bytes / 1024:.1f} KB, span used " f"{raw['spanKm'] * shared_scale:.1f}/{WORLD_SIZE} world-units" ) - regions_out[name] = {"name": name, "points": points, "worldSize": WORLD_SIZE, "meta": meta} + regions_out[name] = { + "name": name, + "points": points, + "landRings": land_rings, + "worldSize": WORLD_SIZE, + "meta": meta, + } OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) with open(OUTPUT_PATH, "w") as f: diff --git a/notebooks/output/box_dimension_coasts.json b/notebooks/output/box_dimension_coasts.json index a66f95f0f..1757db414 100644 --- a/notebooks/output/box_dimension_coasts.json +++ b/notebooks/output/box_dimension_coasts.json @@ -4,6955 +4,9283 @@ "points": [ [ 76.634, - 73.821 + 86.853 ], [ 76.561, - 73.278 + 86.31 ], [ 75.982, - 72.55 + 85.583 ], [ 75.993, - 72.352 + 85.385 ], [ 76.162, - 72.35 + 85.384 ], [ 76.296, - 72.081 + 85.114 ], [ 76.331, - 71.009 + 84.044 ], [ 77.372, - 70.682 + 83.718 ], [ 78.222, - 70.684 + 83.72 ], [ 78.536, - 70.502 + 83.538 ], [ 78.631, - 70.647 + 83.683 ], [ 78.802, - 70.464 + 83.501 ], [ 79.434, - 70.355 + 83.392 ], [ 79.081, - 70.229 + 83.266 ], [ 79.458, - 69.957 + 82.994 ], [ 80.475, - 69.995 + 83.032 ], [ 81.032, - 70.393 + 83.429 ], [ 81.393, - 70.467 + 83.503 ], [ 80.994, - 70.228 + 83.265 ], [ 81.055, - 70.101 + 83.138 ], [ 82.316, - 70.429 + 83.466 ], [ 82.525, - 70.272 + 83.309 ], [ 82.582, - 69.937 + 82.974 ], [ 82.348, - 69.525 + 82.563 ], [ 80.789, - 68.904 + 81.942 ], [ 80.11, - 68.902 + 81.941 ], [ 79.166, - 69.377 + 82.415 ], [ 77.544, - 69.449 + 82.487 ], [ 76.332, - 70.029 + 83.066 ], [ 76.019, - 69.667 + 82.705 ], [ 75.885, - 70.083 + 83.12 ], [ 75.982, - 70.191 + 83.228 ], [ 75.933, - 70.337 + 83.374 ], [ 75.676, - 70.392 + 83.428 ], [ 75.484, - 71.045 + 84.08 ], [ 74.999, - 71.01 + 84.045 ], [ 74.745, - 71.172 + 84.207 ], [ 74.733, - 71.482 + 84.516 ], [ 73.91, - 71.663 + 84.698 ], [ 73.57, - 72.28 + 85.314 ], [ 72.675, - 72.533 + 85.566 ], [ 71.39, - 71.553 + 84.588 ], [ - 70.979, - 70.9 + 70.978, + 83.935 ], [ 70.008, - 70.464 + 83.501 ], [ 69.378, - 70.502 + 83.538 ], [ - 68.945, - 70.719 + 68.944, + 83.755 ], [ 69.196, - 71.316 + 84.351 ], [ 69.137, - 71.48 + 84.515 ], [ 68.215, - 71.336 + 84.371 ], [ 67.782, - 70.937 + 83.973 ], [ 67.466, - 71.01 + 84.045 ], [ 66.134, - 70.21 + 83.247 ], [ 65.396, - 69.974 + 83.011 ], [ 65.456, - 69.666 + 82.703 ], [ 66.133, - 69.521 + 82.558 ], [ 66.401, - 69.667 + 82.705 ], [ 66.714, - 70.246 + 83.283 ], [ 66.982, - 70.32 + 83.357 ], [ 67.079, - 70.066 + 83.103 ], [ 67.393, - 70.211 + 83.248 ], [ - 67.259, - 69.939 + 67.258, + 82.976 ], [ 67.601, - 69.829 + 82.866 ], [ - 67.393, - 69.631 + 67.392, + 82.668 ], [ 66.775, - 69.54 + 82.578 ], [ 66.86, - 69.375 + 82.413 ], [ 66.981, - 69.484 + 82.522 ], [ 67.466, - 69.23 + 82.268 ], [ 67.321, - 68.941 + 81.98 ], [ 67.176, - 69.085 + 82.124 ], [ 66.668, - 69.086 + 82.125 ], [ 66.328, - 69.522 + 82.56 ], [ 66.655, - 68.743 + 81.782 ], [ 66.559, - 67.797 + 80.837 ], [ 65.963, - 66.835 + 79.877 ], [ 65.188, - 66.509 + 79.552 ], [ 65.079, - 66.308 + 79.351 ], [ 65.335, - 66.036 + 79.08 ], [ 65.965, - 65.818 + 78.862 ], [ 66.789, - 65.02 + 78.065 ], [ 67.031, - 65.02 + 78.065 ], [ - 67.129, - 64.728 + 67.128, + 77.774 ], [ 67.418, - 64.693 + 77.739 ], [ 67.733, - 64.295 + 77.341 ], [ 67.902, - 64.73 + 77.775 ], [ 68.047, - 64.548 + 77.594 ], [ 68.24, - 64.695 + 77.74 ], [ 68.739, - 64.675 + 77.721 ], [ 68.036, - 64.096 + 77.142 ], [ 68.193, - 63.678 + 76.725 ], [ 68.423, - 63.658 + 76.705 ], [ 68.072, - 63.17 + 76.218 ], [ 67.636, - 63.098 + 76.146 ], [ 67.345, - 63.462 + 76.509 ], [ 66.921, - 63.078 + 76.126 ], [ 66.776, - 62.681 + 75.73 ], [ - 67.091, - 62.245 + 67.09, + 75.294 ], [ 67.041, - 61.882 + 74.932 ], [ 66.704, - 61.409 + 74.459 ], [ 66.836, - 61.064 + 74.115 ], [ 67.346, - 61.065 + 74.116 ], [ 67.66, - 61.39 + 74.441 ], [ 68.314, - 61.101 + 74.152 ], [ 69.452, - 61.209 + 74.26 ], [ 69.841, - 60.775 + 73.826 ], [ 70.081, - 60.956 + 74.008 ], [ 70.106, - 60.737 + 73.789 ], [ 69.768, - 60.629 + 73.681 ], [ 69.331, - 61.03 + 74.081 ], [ 68.411, - 60.917 + 73.969 ], [ - 67.733, - 60.41 + 67.732, + 73.463 ], [ - 66.739, - 60.195 + 66.738, + 73.248 ], [ 66.92, - 59.848 + 72.901 ], [ 66.837, - 59.72 + 72.773 ], [ - 66.764, - 60.085 + 66.763, + 73.138 ], [ 66.376, - 59.76 + 72.813 ], [ 65.455, - 59.94 + 72.993 ], [ 65.467, - 60.14 + 73.192 ], [ 65.225, - 60.32 + 73.372 ], [ 65.31, - 60.519 + 73.571 ], [ 64.704, - 60.23 + 73.283 ], [ 64.027, - 60.156 + 73.209 ], [ 63.809, - 60.193 + 73.245 ], [ 63.591, - 60.592 + 73.644 ], [ 63.323, - 60.737 + 73.789 ], [ 62.875, - 60.72 + 73.772 ], [ 62.936, - 60.484 + 73.536 ], [ 63.057, - 60.557 + 73.609 ], [ 63.228, - 60.302 + 73.354 ], [ 63.421, - 60.265 + 73.318 ], [ 63.362, - 60.429 + 73.481 ], [ 63.493, - 60.484 + 73.536 ], [ 63.579, - 60.067 + 73.12 ], [ 63.458, - 59.884 + 72.937 ], [ 63.905, - 59.613 + 72.667 ], [ 64.147, - 59.65 + 72.703 ], [ 64.427, - 59.232 + 72.286 ], [ 64.571, - 58.795 + 71.85 ], [ 64.536, - 58.668 + 71.723 ], [ 64.293, - 59.178 + 72.232 ], [ 63.76, - 59.65 + 72.703 ], [ 63.349, - 59.032 + 72.087 ], [ 63.433, - 59.377 + 72.431 ], [ - 63.325, - 59.541 + 63.324, + 72.594 ], [ 62.596, - 59.467 + 72.521 ], [ 63.24, - 59.739 + 72.792 ], [ 63.105, - 60.194 + 73.247 ], [ 62.017, - 60.156 + 73.209 ], [ 62.113, - 59.975 + 73.028 ], [ 61.482, - 59.721 + 72.774 ], [ 61.931, - 60.138 + 73.191 ], [ 61.882, - 60.611 + 73.663 ], [ 61.434, - 61.138 + 74.189 ], [ - 60.708, - 60.773 + 60.707, + 73.825 ], [ 59.906, - 60.666 + 73.718 ], [ 59.786, - 60.885 + 73.936 ], [ 59.302, - 61.065 + 74.116 ], [ 58.503, - 61.174 + 74.225 ], [ 58.188, - 61.574 + 74.625 ], [ 56.712, - 61.065 + 74.116 ], [ 56.577, - 60.938 + 73.989 ], [ 56.698, - 60.575 + 73.627 ], [ 56.65, - 60.321 + 73.373 ], [ 56.346, - 60.084 + 73.137 ], [ 56.227, - 59.758 + 72.812 ], [ 55.401, - 59.212 + 72.266 ], [ 55.089, - 59.395 + 72.449 ], [ 54.954, - 59.306 + 72.36 ], [ 55.355, - 58.922 + 71.977 ], [ 54.773, - 59.069 + 72.123 ], [ 54.712, - 58.904 + 71.959 ], [ 54.857, - 58.651 + 71.706 ], [ - 54.664, - 58.434 + 54.663, + 71.489 ], [ 54.82, - 58.414 + 71.47 ], [ 54.894, - 58.126 + 71.182 ], [ 54.99, - 58.305 + 71.361 ], [ 55.184, - 58.161 + 71.217 ], [ 55.257, - 58.451 + 71.506 ], [ 55.427, - 58.234 + 71.29 ], [ 55.62, - 58.488 + 71.543 ], [ - 55.511, - 58.142 + 55.51, + 71.198 ], [ 55.79, - 58.053 + 71.108 ], [ 55.814, - 58.488 + 71.543 ], [ 56.069, - 58.396 + 71.452 ], [ 55.924, - 58.759 + 71.814 ], [ 56.322, - 58.633 + 71.688 ], [ 56.358, - 58.942 + 71.996 ], [ 56.237, - 59.086 + 72.14 ], [ 56.517, - 59.248 + 72.302 ], [ 56.723, - 59.16 + 72.214 ], [ - 56.505, - 59.087 + 56.504, + 72.141 ], [ 56.662, - 58.816 + 71.871 ], [ 56.976, - 58.961 + 72.016 ], [ - 57.294, - 58.705 + 57.293, + 71.76 ], [ 57.207, - 58.941 + 71.995 ], [ 57.28, - 59.23 + 72.284 ], [ 57.341, - 59.288 + 72.342 ], [ 57.329, - 59.013 + 72.068 ], [ - 57.499, - 58.977 + 57.498, + 72.031 ], [ 57.474, - 58.723 + 71.778 ], [ 57.327, - 58.65 + 71.705 ], [ - 57.365, - 58.414 + 57.364, + 71.47 ], [ 57.872, - 58.453 + 71.508 ], [ 57.861, - 59.124 + 72.179 ], [ 58.091, - 59.215 + 72.269 ], [ 58.151, - 59.013 + 72.068 ], [ 58.056, - 58.868 + 71.923 ], [ - 58.334, - 58.633 + 58.333, + 71.688 ], [ - 58.527, - 58.85 + 58.526, + 71.905 ], [ 58.733, - 58.83 + 71.885 ], [ 58.623, - 59.141 + 72.196 ], [ 59.037, - 58.996 + 72.051 ], [ 59.131, - 59.25 + 72.304 ], [ 59.302, - 59.285 + 72.339 ], [ 59.763, - 58.996 + 72.051 ], [ 60.005, - 59.031 + 72.086 ], [ 59.921, - 58.65 + 71.705 ], [ 60.162, - 58.287 + 71.343 ], [ 60.115, - 57.962 + 71.018 ], [ 60.43, - 57.707 + 70.763 ], [ 60.38, - 57.163 + 70.221 ], [ - 60.611, - 57.073 + 60.61, + 70.13 ], [ 60.659, - 57.364 + 70.421 ], [ 60.878, - 57.362 + 70.419 ], [ 60.72, - 57.307 + 70.364 ], [ 60.852, - 56.964 + 70.021 ], [ 61.023, - 57.144 + 70.201 ], [ 61.095, - 56.962 + 70.02 ], [ 61.252, - 56.981 + 70.038 ], [ - 61.106, - 56.763 + 61.105, + 69.821 ], [ 61.253, - 56.689 + 69.747 ], [ 61.203, - 56.218 + 69.277 ], [ 61.349, - 56.074 + 69.134 ], [ - 60.804, - 54.859 + 60.803, + 67.92 ], [ - 60.817, - 55.24 + 60.816, + 68.3 ], [ 61.01, - 55.419 + 68.479 ], [ 61.131, - 55.82 + 68.88 ], [ - 60.951, - 56.275 + 60.95, + 69.334 ], [ 60.683, - 56.02 + 69.079 ], [ 60.622, - 56.037 + 69.096 ], [ 60.889, - 56.327 + 69.386 ], [ 60.854, - 56.528 + 69.587 ], [ 60.598, - 56.727 + 69.785 ], [ 60.661, - 56.926 + 69.984 ], [ - 60.418, - 56.927 + 60.417, + 69.985 ], [ 60.318, - 56.454 + 69.513 ], [ 60.127, - 56.528 + 69.587 ], [ 60.053, - 56.347 + 69.405 ], [ 59.933, - 56.345 + 69.404 ], [ - 60.066, - 56.51 + 60.065, + 69.568 ], [ - 60.007, - 56.6 + 60.006, + 69.658 ], [ 59.255, - 55.911 + 68.97 ], [ 58.636, - 55.675 + 68.735 ], [ 58.698, - 55.438 + 68.498 ], [ 58.841, - 55.439 + 68.499 ], [ 59.969, - 55.928 + 68.987 ], [ 59.726, - 55.71 + 68.77 ], [ 59.752, - 55.457 + 68.518 ], [ - 59.886, - 55.368 + 59.885, + 68.428 ], [ 59.667, - 55.293 + 68.353 ], [ 59.606, - 55.675 + 68.735 ], [ 59.459, - 55.638 + 68.697 ], [ 59.291, - 55.276 + 68.336 ], [ 59.387, - 54.948 + 68.009 ], [ - 59.24, - 54.803 + 59.239, + 67.864 ], [ 59.254, - 54.532 + 67.593 ], [ 59.143, - 55.166 + 68.226 ], [ 58.866, - 55.258 + 68.318 ], [ 58.429, - 55.111 + 68.172 ], [ 58.346, - 54.947 + 68.008 ], [ 58.54, - 54.694 + 67.755 ], [ 58.309, - 54.82 + 67.881 ], [ 58.116, - 54.459 + 67.521 ], [ 58.223, - 54.949 + 68.01 ], [ 58.637, - 55.349 + 68.409 ], [ 58.553, - 55.62 + 68.679 ], [ 58.093, - 55.802 + 68.862 ], [ 57.533, - 55.62 + 68.679 ], [ 57.097, - 56.02 + 69.079 ], [ 57.012, - 55.964 + 69.024 ], [ 57.193, - 55.693 + 68.753 ], [ 57.003, - 55.658 + 68.718 ], [ 57.039, - 55.783 + 68.842 ], [ - 56.929, - 55.837 + 56.928, + 68.897 ], [ 56.445, - 55.765 + 68.824 ], [ 56.467, - 55.62 + 68.679 ], [ - 56.325, - 55.584 + 56.324, + 68.644 ], [ - 56.069, - 55.747 + 56.068, + 68.806 ], [ 56.383, - 55.857 + 68.916 ], [ 56.117, - 56.218 + 69.277 ], [ 56.311, - 56.436 + 69.495 ], [ 56.227, - 56.71 + 69.768 ], [ 55.476, - 56.382 + 69.44 ], [ 55.245, - 56.728 + 69.786 ], [ 55.463, - 56.69 + 69.748 ], [ 55.355, - 57.328 + 70.385 ], [ 55.112, - 57.109 + 70.166 ], [ 54.844, - 57.29 + 70.347 ], [ 54.955, - 56.982 + 70.039 ], [ 54.844, - 56.889 + 69.946 ], [ - 54.555, - 57.327 + 54.554, + 70.384 ], [ 54.373, - 56.946 + 70.003 ], [ 54.567, - 56.909 + 69.967 ], [ - 54.58, - 56.456 + 54.579, + 69.514 ], [ 54.361, - 56.819 + 69.876 ], [ 54.275, - 56.364 + 69.422 ], [ 54.374, - 56.185 + 69.243 ], [ 54.226, - 56.254 + 69.312 ], [ 54.083, - 56.691 + 69.75 ], [ 54.154, - 57.127 + 70.184 ], [ 54.071, - 57.145 + 70.203 ], [ 53.695, - 56.147 + 69.206 ], [ - 53.817, - 55.966 + 53.816, + 69.025 ], [ 53.743, - 55.566 + 68.626 ], [ 54.106, - 55.385 + 68.445 ], [ 53.998, - 55.258 + 68.318 ], [ 53.475, - 55.347 + 68.408 ], [ 53.38, - 55.093 + 68.154 ], [ 53.475, - 54.912 + 67.973 ], [ 53.185, - 54.84 + 67.901 ], [ 53.259, - 54.476 + 67.538 ], [ 52.944, - 54.116 + 67.178 ], [ 52.93, - 53.842 + 66.905 ], [ 52.822, - 54.151 + 67.213 ], [ 52.92, - 54.476 + 67.538 ], [ 53.114, - 54.658 + 67.719 ], [ 53.136, - 55.022 + 68.083 ], [ 53.38, - 55.166 + 68.226 ], [ 53.598, - 55.93 + 68.99 ], [ 53.271, - 56.019 + 69.078 ], [ 53.355, - 56.183 + 69.242 ], [ 53.185, - 56.256 + 69.315 ], [ 53.575, - 56.581 + 69.64 ], [ 53.307, - 57.017 + 70.074 ], [ 53.415, - 57.292 + 70.349 ], [ 53.78, - 57.436 + 70.492 ], [ 53.817, - 57.744 + 70.8 ], [ - 54.144, - 58.232 + 54.143, + 71.287 ], [ 54.349, - 58.29 + 71.345 ], [ 54.263, - 58.523 + 71.578 ], [ 54.094, - 58.561 + 71.616 ], [ 53.633, - 58.451 + 71.506 ], [ 53.464, - 58.088 + 71.144 ], [ 53.294, - 58.561 + 71.616 ], [ 52.956, - 58.378 + 71.433 ], [ 52.906, - 58.161 + 71.217 ], [ 53.113, - 58.251 + 71.307 ], [ 52.968, - 57.961 + 71.017 ], [ 53.04, - 57.815 + 70.872 ], [ 52.833, - 57.507 + 70.564 ], [ 52.485, - 57.38 + 70.437 ], [ 52.738, - 57.038 + 70.095 ], [ 52.448, - 57.181 + 70.239 ], [ 52.289, - 56.982 + 70.039 ], [ 52.568, - 57.942 + 70.999 ], [ 52.35, - 57.761 + 70.817 ], [ 52.301, - 58.015 + 71.071 ], [ 52.01, - 58.016 + 71.072 ], [ 51.708, - 57.128 + 70.186 ], [ 51.803, - 56.618 + 69.676 ], [ 51.612, - 56.509 + 69.567 ], [ 51.612, - 56.076 + 69.135 ], [ - 51.443, - 55.784 + 51.442, + 68.844 ], [ 51.611, - 55.53 + 68.59 ], [ 51.332, - 55.548 + 68.608 ], [ 51.236, - 54.896 + 67.957 ], [ - 51.162, - 55.185 + 51.161, + 68.246 ], [ 50.897, - 55.185 + 68.246 ], [ - 50.885, - 55.24 + 50.884, + 68.3 ], [ 51.189, - 55.219 + 68.28 ], [ 51.248, - 55.603 + 68.662 ], [ 51.393, - 55.709 + 68.769 ], [ - 51.514, - 56.255 + 51.513, + 69.314 ], [ 51.393, - 56.509 + 69.567 ], [ - 51.514, - 56.872 + 51.513, + 69.93 ], [ 51.418, - 56.944 + 70.002 ], [ 51.647, - 57.945 + 71.001 ], [ 51.067, - 57.688 + 70.745 ], [ 51.199, - 57.417 + 70.474 ], [ 51.089, - 57.255 + 70.312 ], [ 51.067, - 57.509 + 70.566 ], [ 50.704, - 57.544 + 70.601 ], [ 50.677, - 57.832 + 70.889 ], [ 50.316, - 57.726 + 70.782 ], [ 49.418, - 58.125 + 71.181 ], [ 49.091, - 57.633 + 70.689 ], [ - 49.178, - 57.471 + 49.177, + 70.527 ], [ 49.541, - 57.579 + 70.636 ], [ 49.528, - 57.344 + 70.401 ], [ 49.226, - 57.399 + 70.456 ], [ 49.275, - 57.183 + 70.24 ], [ 49.078, - 57.183 + 70.24 ], [ 48.523, - 56.601 + 69.659 ], [ - 48.461, - 57.634 + 48.46, + 70.691 ], [ 48.873, - 57.561 + 70.618 ], [ 48.486, - 58.216 + 71.272 ], [ - 48.39, - 58.978 + 48.389, + 72.033 ], [ 48.486, - 59.921 + 72.974 ], [ - 48.608, - 60.356 + 48.607, + 73.408 ], [ 48.801, - 60.465 + 73.517 ], [ 48.802, - 60.973 + 74.024 ], [ 49.031, - 61.319 + 74.37 ], [ 49.541, - 61.646 + 74.696 ], [ 49.6, - 61.989 + 75.039 ], [ 49.891, - 62.208 + 75.258 ], [ - 49.782, - 62.26 + 49.781, + 75.31 ], [ 48.838, - 61.899 + 74.948 ], [ 48.427, - 62.044 + 75.093 ], [ 48.075, - 61.626 + 74.677 ], [ - 48.075, - 61.263 + 48.074, + 74.314 ], [ 47.833, - 60.755 + 73.807 ], [ 47.904, - 60.539 + 73.591 ], [ 47.807, - 59.994 + 73.047 ], [ 47.542, - 59.704 + 72.757 ], [ 48.015, - 59.576 + 72.629 ], [ 48.293, - 58.868 + 71.923 ], [ 48.195, - 57.309 + 70.366 ], [ - 47.639, - 56.291 + 47.638, + 69.35 ], [ - 47.857, - 56.074 + 47.856, + 69.134 ], [ 47.505, - 56.128 + 69.187 ], [ 46.681, - 55.547 + 68.607 ], [ 46.416, - 55.548 + 68.608 ], [ 46.452, - 55.277 + 68.338 ], [ 46.161, - 54.802 + 67.863 ], [ 46.38, - 54.15 + 67.212 ], [ 46.257, - 54.041 + 67.103 ], [ 46.246, - 53.732 + 66.795 ], [ 46.839, - 53.859 + 66.922 ], [ 46.706, - 53.623 + 66.686 ], [ 46.512, - 53.733 + 66.796 ], [ 46.184, - 53.57 + 66.633 ], [ - 46.536, - 53.406 + 46.535, + 66.469 ], [ 46.525, - 53.206 + 66.27 ], [ 46.658, - 53.079 + 66.143 ], [ 46.803, - 53.15 + 66.214 ], [ 46.925, - 52.642 + 65.707 ], [ 47.043, - 52.861 + 65.925 ], [ 47.602, - 53.044 + 66.108 ], [ 47.542, - 52.808 + 65.872 ], [ 47.651, - 52.643 + 65.708 ], [ 47.82, - 52.79 + 65.854 ], [ 48.109, - 52.282 + 65.347 ], [ 48.449, - 52.209 + 65.274 ], [ 48.872, - 52.37 + 65.435 ], [ 48.643, - 51.955 + 65.021 ], [ 48.233, - 52.172 + 65.237 ], [ 48.037, - 52.063 + 65.128 ], [ 48.063, - 52.244 + 65.309 ], [ - 47.773, - 52.245 + 47.772, + 65.31 ], [ 47.807, - 52.406 + 65.471 ], [ 47.675, - 52.462 + 65.527 ], [ 47.347, - 52.299 + 65.364 ], [ 47.942, - 51.991 + 65.057 ], [ 47.88, - 51.72 + 64.786 ], [ - 47.966, - 51.408 + 47.965, + 64.475 ], [ 48.123, - 51.429 + 64.495 ], [ 48.087, - 51.775 + 64.841 ], [ 48.572, - 51.917 + 64.983 ], [ 48.487, - 51.503 + 64.569 ], [ 48.849, - 51.32 + 64.386 ], [ 48.595, - 51.19 + 64.257 ], [ 48.595, - 51.046 + 64.113 ], [ 49.238, - 51.354 + 64.42 ], [ 49.031, - 50.974 + 64.041 ], [ 48.573, - 51.009 + 64.076 ], [ 48.426, - 51.229 + 64.296 ], [ 47.749, - 51.337 + 64.403 ], [ 47.688, - 51.683 + 64.749 ], [ 47.555, - 51.736 + 64.802 ], [ 47.579, - 51.265 + 64.332 ], [ 47.82, - 51.264 + 64.331 ], [ 48.109, - 50.756 + 63.823 ], [ 48.255, - 50.938 + 64.005 ], [ 48.317, - 50.81 + 63.878 ], [ 48.257, - 50.612 + 63.68 ], [ 47.989, - 50.829 + 63.896 ], [ 47.88, - 50.702 + 63.769 ], [ - 48.39, - 50.121 + 48.389, + 63.189 ], [ 48.04, - 50.32 + 63.389 ], [ 47.965, - 49.886 + 62.955 ], [ 47.88, - 50.304 + 63.372 ], [ - 47.664, - 50.593 + 47.663, + 63.66 ], [ 47.806, - 50.702 + 63.769 ], [ 47.698, - 50.864 + 63.931 ], [ 47.334, - 50.792 + 63.86 ], [ 47.286, - 50.504 + 63.572 ], [ 46.875, - 50.503 + 63.571 ], [ 46.791, - 50.304 + 63.372 ], [ 46.865, - 50.195 + 63.263 ], [ 46.67, - 50.12 + 63.188 ], [ 46.742, - 50.774 + 63.842 ], [ 47.068, - 50.575 + 63.642 ], [ 47.324, - 50.992 + 64.059 ], [ 47.177, - 51.209 + 64.275 ], [ 47.373, - 51.574 + 64.64 ], [ - 47.253, - 51.753 + 47.252, + 64.819 ], [ - 47.492, - 51.937 + 47.491, + 65.002 ], [ - 47.19, - 52.172 + 47.189, + 65.237 ], [ - 46.972, - 51.919 + 46.971, + 64.984 ], [ 46.657, - 52.029 + 65.094 ], [ 46.536, - 51.701 + 64.767 ], [ 46.621, - 52.118 + 65.184 ], [ 46.355, - 52.263 + 65.329 ], [ - 46.561, - 52.427 + 46.56, + 65.492 ], [ 46.705, - 52.352 + 65.417 ], [ 46.706, - 52.606 + 65.67 ], [ 46.296, - 52.682 + 65.747 ], [ 46.1, - 52.354 + 65.419 ], [ 46.016, - 52.409 + 65.474 ], [ - 46.259, - 52.699 + 46.258, + 65.763 ], [ 46.148, - 52.861 + 65.925 ], [ 46.45, - 52.918 + 65.982 ], [ 46.233, - 53.242 + 66.306 ], [ 46.246, - 53.443 + 66.506 ], [ 46.004, - 53.369 + 66.433 ], [ 45.919, - 53.606 + 66.669 ], [ 46.137, - 54.113 + 67.176 ], [ 46.003, - 54.676 + 67.737 ], [ 44.719, - 53.37 + 66.434 ], [ 44.211, - 53.044 + 66.108 ], [ 42.732, - 52.426 + 65.49 ], [ - 42.103, - 52.426 + 42.102, + 65.49 ], [ 41.836, - 52.825 + 65.889 ], [ 41.727, - 52.626 + 65.691 ], [ 41.983, - 52.135 + 65.201 ], [ 42.151, - 52.28 + 65.345 ], [ 43.025, - 52.245 + 65.31 ], [ 44.067, - 52.791 + 65.855 ], [ 43.873, - 52.209 + 65.274 ], [ 43.41, - 52.136 + 65.202 ], [ 43.255, - 51.936 + 65.001 ], [ 43.436, - 51.701 + 64.767 ], [ 43.001, - 51.954 + 65.019 ], [ 42.2, - 51.807 + 64.873 ], [ 42.03, - 52.063 + 65.128 ], [ - 41.679, - 51.863 + 41.678, + 64.929 ], [ 41.885, - 51.628 + 64.694 ], [ 42.031, - 51.665 + 64.731 ], [ 42.054, - 51.482 + 64.548 ], [ 42.261, - 51.464 + 64.53 ], [ 42.285, - 51.32 + 64.386 ], [ - 42.115, - 51.173 + 42.114, + 64.24 ], [ - 42.262, - 50.992 + 42.261, + 64.059 ], [ 42.115, - 50.883 + 63.95 ], [ 42.2, - 50.611 + 63.679 ], [ 42.49, - 50.901 + 63.968 ], [ 42.757, - 50.792 + 63.86 ], [ 42.732, - 50.611 + 63.679 ], [ 42.951, - 50.647 + 63.715 ], [ 43.193, - 50.139 + 63.207 ], [ 43.399, - 50.011 + 63.079 ], [ 43.701, - 49.086 + 62.157 ], [ 44.004, - 49.068 + 62.138 ], [ 44.067, - 48.65 + 61.721 ], [ 43.726, - 48.58 + 61.65 ], [ 43.956, - 48.777 + 61.847 ], [ 43.896, - 49.051 + 62.122 ], [ 43.605, - 48.942 + 62.013 ], [ 43.412, - 49.377 + 62.446 ], [ 43.049, - 49.704 + 62.773 ], [ 42.829, - 49.667 + 62.736 ], [ 42.868, - 50.048 + 63.117 ], [ - 42.539, - 50.283 + 42.538, + 63.351 ], [ 42.297, - 50.14 + 63.209 ], [ 42.298, - 50.357 + 63.425 ], [ 42.177, - 50.285 + 63.354 ], [ 42.115, - 49.829 + 62.898 ], [ 42.007, - 49.776 + 62.845 ], [ 42.093, - 50.231 + 63.299 ], [ 41.945, - 50.266 + 63.334 ], [ 42.042, - 50.375 + 63.443 ], [ 41.739, - 51.011 + 64.078 ], [ 41.644, - 51.046 + 64.113 ], [ 41.618, - 50.865 + 63.932 ], [ 41.424, - 50.975 + 64.042 ], [ - 41.352, - 50.757 + 41.351, + 63.825 ], [ 40.625, - 50.683 + 63.751 ], [ 41.304, - 50.974 + 64.041 ], [ 41.389, - 51.281 + 64.348 ], [ 41.559, - 51.392 + 64.459 ], [ 41.085, - 52.099 + 65.164 ], [ 40.165, - 52.136 + 65.202 ], [ 39.827, - 51.991 + 65.057 ], [ 39.099, - 52.389 + 65.454 ], [ 38.847, - 52.226 + 65.291 ], [ 38.905, - 51.989 + 65.054 ], [ 38.227, - 51.483 + 64.549 ], [ 38.203, - 51.155 + 64.222 ], [ 37.73, - 50.884 + 63.952 ], [ 37.779, - 50.703 + 63.77 ], [ 37.537, - 50.193 + 63.262 ], [ 36.881, - 49.36 + 62.43 ], [ 36.981, - 49.177 + 62.247 ], [ 36.859, - 48.924 + 61.995 ], [ 36.884, - 48.633 + 61.704 ], [ 37.028, - 48.488 + 61.559 ], [ - 36.969, - 47.998 + 36.968, + 61.069 ], [ - 36.835, - 48.198 + 36.834, + 61.27 ], [ 36.907, - 48.416 + 61.487 ], [ 36.786, - 48.74 + 61.811 ], [ 36.811, - 49.216 + 62.286 ], [ 36.604, - 49.558 + 62.628 ], [ 35.927, - 49.195 + 62.265 ], [ 34.789, - 49.197 + 62.267 ], [ 34.497, - 49.051 + 62.122 ], [ 34.424, - 49.197 + 62.267 ], [ 33.503, - 48.942 + 62.013 ], [ 33.431, - 48.542 + 61.613 ], [ 33.093, - 48.725 + 61.795 ], [ 32.802, - 48.506 + 61.577 ], [ 32.679, - 48.87 + 61.94 ], [ 32.534, - 48.869 + 61.939 ], [ 31.264, - 47.873 + 60.945 ], [ 31.638, - 47.309 + 60.382 ], [ 31.905, - 47.344 + 60.417 ], [ 31.881, - 47.163 + 60.236 ], [ 32.208, - 47.001 + 60.074 ], [ 32.039, - 46.964 + 60.038 ], [ 32.05, - 46.618 + 59.692 ], [ 31.736, - 47.237 + 60.31 ], [ - 30.984, - 47.706 + 30.983, + 60.778 ], [ 30.657, - 47.11 + 60.183 ], [ 30.608, - 46.638 + 59.712 ], [ 30.488, - 46.42 + 59.494 ], [ 30.524, - 46.039 + 59.114 ], [ 30.198, - 46.31 + 59.384 ], [ 30.318, - 46.71 + 59.784 ], [ 30.55, - 46.727 + 59.801 ], [ 30.61, - 47.001 + 60.074 ], [ - 30.271, - 47.146 + 30.27, + 60.219 ], [ 30.658, - 47.216 + 60.289 ], [ 30.875, - 47.762 + 60.834 ], [ 30.717, - 47.963 + 61.034 ], [ 30.233, - 48.217 + 61.288 ], [ 30.016, - 48.109 + 61.181 ], [ 29.045, - 48.361 + 61.432 ], [ 28.44, - 48.035 + 61.107 ], [ 27.957, - 47.999 + 61.071 ], [ - 27.352, - 48.323 + 27.351, + 61.394 ], [ 27.714, - 47.636 + 60.708 ], [ 27.521, - 47.816 + 60.888 ], [ 26.429, - 46.545 + 59.619 ], [ 26.078, - 46.347 + 59.422 ], [ 26.164, - 45.784 + 58.859 ], [ 26.285, - 45.747 + 58.823 ], [ 26.333, - 45.929 + 59.004 ], [ 26.516, - 45.693 + 58.768 ], [ 26.271, - 45.659 + 58.735 ], [ 26.465, - 45.403 + 58.478 ], [ - 26.018, - 45.676 + 26.017, + 58.751 ], [ 25.569, - 45.369 + 58.445 ], [ 25.654, - 45.24 + 58.317 ], [ 26.055, - 45.295 + 58.371 ], [ - 26.081, - 45.151 + 26.08, + 58.227 ], [ 25.873, - 45.13 + 58.207 ], [ 25.788, - 44.896 + 57.972 ], [ 25.872, - 44.476 + 57.553 ], [ 25.63, - 44.84 + 57.917 ], [ 25.269, - 44.767 + 57.844 ], [ 25.134, - 44.642 + 57.719 ], [ 25.11, - 44.276 + 57.354 ], [ 24.746, - 43.987 + 57.065 ], [ - 24.869, - 43.734 + 24.868, + 56.813 ], [ 24.699, - 43.806 + 56.884 ], [ 24.711, - 43.353 + 56.432 ], [ 24.59, - 43.752 + 56.831 ], [ 24.08, - 43.68 + 56.758 ], [ 23.971, - 43.624 + 56.703 ], [ 24.092, - 43.443 + 56.522 ], [ 23.887, - 43.426 + 56.505 ], [ 23.839, - 43.17 + 56.25 ], [ 23.729, - 43.443 + 56.522 ], [ 23.765, - 43.754 + 56.832 ], [ 23.475, - 43.642 + 56.721 ], [ 23.45, - 43.461 + 56.54 ], [ 23.318, - 43.553 + 56.632 ], [ 23.572, - 43.898 + 56.976 ], [ 23.873, - 43.916 + 56.994 ], [ 23.535, - 44.132 + 57.21 ], [ 23.634, - 45.076 + 58.152 ], [ 23.839, - 45.532 + 58.608 ], [ 23.401, - 45.423 + 58.499 ], [ 23.185, - 45.601 + 58.677 ], [ 22.023, - 45.746 + 58.822 ], [ 22.24, - 45.567 + 58.643 ], [ 23.21, - 45.53 + 58.605 ], [ 23.292, - 45.439 + 58.515 ], [ 22.747, - 45.458 + 58.534 ], [ 22.675, - 45.239 + 58.315 ], [ 22.53, - 45.493 + 58.569 ], [ 22.239, - 45.495 + 58.57 ], [ 22.264, - 45.347 + 58.423 ], [ 21.998, - 45.167 + 58.243 ], [ 22.131, - 45.548 + 58.623 ], [ 21.902, - 45.895 + 58.97 ], [ 21.441, - 45.421 + 58.497 ], [ - 20.616, - 45.132 + 20.615, + 58.208 ], [ 21.196, - 45.203 + 58.279 ], [ 21.816, - 45.622 + 58.697 ], [ 21.319, - 45.167 + 58.243 ], [ 21.247, - 44.914 + 57.99 ], [ 21.052, - 44.985 + 58.062 ], [ 21.077, - 44.66 + 57.737 ], [ 20.908, - 44.879 + 57.955 ], [ 20.811, - 44.442 + 57.519 ], [ 20.725, - 44.714 + 57.791 ], [ 20.872, - 44.968 + 58.045 ], [ 20.349, - 45.168 + 58.244 ], [ 20.181, - 44.804 + 57.881 ], [ 19.804, - 44.64 + 57.717 ], [ 19.949, - 44.459 + 57.536 ], [ 19.756, - 44.424 + 57.501 ], [ 20.048, - 44.279 + 57.356 ], [ 19.648, - 44.115 + 57.193 ], [ 19.563, - 43.877 + 56.955 ], [ 19.672, - 43.716 + 56.795 ], [ 19.38, - 43.716 + 56.795 ], [ 19.113, - 43.498 + 56.577 ], [ 18.897, - 42.916 + 55.996 ], [ 18.896, - 43.571 + 56.65 ], [ 18.509, - 43.714 + 56.792 ], [ 18.217, - 43.568 + 56.647 ], [ 18.122, - 43.279 + 56.359 ], [ 18.037, - 43.408 + 56.487 ], [ 18.195, - 43.751 + 56.83 ], [ 18.461, - 43.824 + 56.902 ], [ 19.091, - 43.68 + 56.758 ], [ 19.636, - 44.314 + 57.391 ], [ 19.877, - 45.077 + 58.154 ], [ 19.381, - 45.094 + 58.17 ], [ 18.193, - 45.894 + 58.969 ], [ - 18.205, - 45.657 + 18.204, + 58.732 ], [ 18.641, - 45.15 + 58.226 ], [ 18.267, - 45.384 + 58.46 ], [ 18.013, - 44.749 + 57.826 ], [ 18.134, - 44.568 + 57.645 ], [ 17.541, - 44.043 + 57.121 ], [ - 17.844, - 44.605 + 17.843, + 57.682 ], [ - 17.479, - 44.64 + 17.478, + 57.717 ], [ 17.575, - 44.861 + 57.937 ], [ 17.431, - 45.077 + 58.154 ], [ 17.444, - 45.313 + 58.389 ], [ 17.154, - 44.877 + 57.954 ], [ 17.165, - 45.003 + 58.08 ], [ 17.672, - 45.695 + 58.771 ], [ 17.66, - 45.891 + 58.966 ], [ - 17.781, - 46.001 + 17.78, + 59.076 ], [ 17.856, - 45.785 + 58.86 ], [ 18.037, - 46.021 + 59.096 ], [ - 17.71, - 46.219 + 17.709, + 59.294 ], [ 17.951, - 46.366 + 59.44 ], [ 18.315, - 46.255 + 59.33 ], [ 18.594, - 46.458 + 59.532 ], [ 18.423, - 46.564 + 59.638 ], [ 18.327, - 47.255 + 60.328 ], [ 18.097, - 47.346 + 60.418 ], [ 18.086, - 47.618 + 60.69 ], [ 17.904, - 47.781 + 60.853 ], [ 17.565, - 48.034 + 61.106 ], [ 17.04, - 47.888 + 60.96 ], [ 16.991, - 47.398 + 60.47 ], [ 16.911, - 47.344 + 60.417 ], [ 16.994, - 47.836 + 60.908 ], [ - 16.791, - 48.18 + 16.79, + 61.252 ], [ 15.432, - 48.324 + 61.396 ], [ 15.093, - 48.215 + 61.287 ], [ - 15.118, - 48.07 + 15.117, + 61.142 ], [ 14.972, - 48.217 + 61.288 ], [ 14.656, - 48.18 + 61.252 ], [ 14.439, - 47.852 + 60.925 ], [ 13.156, - 47.927 + 60.999 ], [ 12.671, - 48.108 + 61.179 ], [ 12.236, - 47.999 + 61.071 ], [ 12.345, - 47.471 + 60.544 ], [ 12.076, - 46.926 + 59.999 ], [ 12.526, - 46.62 + 59.694 ], [ 12.986, - 46.618 + 59.692 ], [ 13.07, - 46.346 + 59.421 ], [ - 12.828, - 44.786 + 12.827, + 57.862 ], [ 12.32, - 43.261 + 56.34 ], [ 11.787, - 42.211 + 55.292 ], [ 10.466, - 40.413 + 53.497 ], [ 9.959, - 39.903 + 52.987 ], [ 9.255, - 39.685 + 52.77 ], [ 9.231, - 39.47 + 52.555 ], [ - 8.638, - 39.028 + 8.637, + 52.114 ], [ 7.547, - 38.666 + 51.753 ], [ 7.643, - 37.903 + 50.991 ], [ 7.535, - 37.957 + 51.045 ], [ 7.584, - 38.285 + 51.372 ], [ 7.45, - 38.629 + 51.715 ], [ 7.037, - 38.665 + 51.752 ], [ 6.918, - 39.028 + 52.114 ], [ 6.626, - 39.029 + 52.115 ], [ 5.874, - 38.81 + 51.897 ], [ 5.755, - 38.447 + 51.534 ], [ 5.052, - 38.34 + 51.427 ], [ 4.76, - 37.865 + 50.953 ], [ 4.397, - 38.119 + 51.207 ], [ 2.921, - 37.975 + 51.063 ], [ 2.533, - 37.685 + 50.773 ], [ 2.023, - 37.687 + 50.776 ], [ 1.673, - 37.341 + 50.43 ], [ 2.641, - 37.124 + 50.213 ], [ 2.398, - 36.761 + 49.85 ], [ 2.363, - 36.196 + 49.286 ], [ 2.896, - 36.416 + 49.506 ], [ 3.089, - 36.197 + 49.287 ], [ 3.404, - 36.415 + 49.505 ], [ 3.585, - 36.326 + 49.417 ], [ 3.718, - 35.943 + 49.034 ], [ 4.784, - 36.088 + 49.179 ], [ 4.808, - 35.834 + 48.925 ], [ 5.1, - 36.017 + 49.107 ], [ 6.407, - 35.472 + 48.564 ], [ 7.039, - 35.691 + 48.782 ], [ 7.475, - 35.182 + 48.274 ], [ 7.814, - 35.29 + 48.381 ], [ 7.934, - 35.108 + 48.2 ], [ 8.516, - 35.072 + 48.164 ], [ 8.902, - 35.291 + 48.383 ], [ - 9.267, - 34.89 + 9.266, + 47.983 ], [ 9.581, - 34.783 + 47.875 ], [ - 10.647, - 34.71 + 10.646, + 47.803 ], [ 10.915, - 34.347 + 47.44 ], [ 11.738, - 34.456 + 47.549 ], [ 12.488, - 34.273 + 47.367 ], [ 12.778, - 34.529 + 47.622 ], [ 13.166, - 34.565 + 47.658 ], [ 13.36, - 35.072 + 48.164 ], [ 13.41, - 34.745 + 47.838 ], [ 13.627, - 34.709 + 47.802 ], [ 13.7, - 34.963 + 48.055 ], [ 14.355, - 35.109 + 48.201 ], [ 14.729, - 34.51 + 47.604 ], [ 14.923, - 33.892 + 46.986 ], [ - 14.778, - 33.529 + 14.777, + 46.624 ], [ 15.239, - 33.24 + 46.335 ], [ 15.069, - 32.26 + 45.357 ], [ 14.719, - 32.241 + 45.337 ], [ 14.487, - 32.041 + 45.138 ], [ 14.608, - 31.387 + 44.485 ], [ 14.27, - 30.554 + 43.654 ], [ 13.965, - 30.425 + 43.524 ], [ 13.895, - 30.209 + 43.309 ], [ 12.272, - 30.062 + 43.162 ], [ 12.357, - 29.791 + 42.891 ], [ 12.054, - 29.519 + 42.62 ], [ 11.737, - 29.591 + 42.692 ], [ 11.058, - 29.337 + 42.438 ], [ 11.011, - 29.157 + 42.258 ], [ 10.55, - 29.158 + 42.26 ], [ 10.622, - 28.721 + 41.824 ], [ - 10.395, - 28.775 + 10.394, + 41.877 ], [ 10.552, - 28.939 + 42.041 ], [ 9.51, - 28.685 + 41.787 ], [ 8.588, - 28.974 + 42.076 ], [ - 8.529, - 29.211 + 8.528, + 42.313 ], [ 8.771, - 29.393 + 42.494 ], [ 8.722, - 29.573 + 42.674 ], [ 8.297, - 29.808 + 42.908 ], [ - 8.214, - 30.263 + 8.213, + 43.363 ], [ 8.02, - 30.518 + 43.617 ], [ 8.067, - 30.698 + 43.797 ], [ 7.438, - 31.244 + 44.342 ], [ 7.401, - 31.734 + 44.831 ], [ 6.991, - 31.733 + 44.83 ], [ 6.832, - 31.353 + 44.451 ], [ 7.1, - 31.205 + 44.303 ], [ 6.953, - 30.952 + 44.051 ], [ - 7.124, - 30.882 + 7.123, + 43.981 ], [ 7.027, - 30.772 + 43.871 ], [ 7.05, - 29.862 + 42.963 ], [ 6.784, - 29.499 + 42.6 ], [ 6.856, - 29.248 + 42.349 ], [ 6.724, - 28.939 + 42.041 ], [ 6.445, - 28.957 + 42.059 ], [ 6.625, - 28.648 + 41.75 ], [ - 7.233, - 28.537 + 7.232, + 41.64 ], [ 7.22, - 28.05 + 41.153 ], [ 6.869, - 27.777 + 40.88 ], [ 6.24, - 28.175 + 41.278 ], [ 5.682, - 28.104 + 41.208 ], [ 5.731, - 27.922 + 41.025 ], [ 5.367, - 27.703 + 40.807 ], [ 5.149, - 27.743 + 40.846 ], [ 5.075, - 27.995 + 41.099 ], [ 4.908, - 27.959 + 41.063 ], [ 5.161, - 27.105 + 40.21 ], [ 4.774, - 26.78 + 39.885 ], [ - 5.295, - 26.833 + 5.294, + 39.938 ], [ 5.538, - 26.542 + 39.648 ], [ 5.631, - 26.761 + 39.867 ], [ 5.946, - 26.833 + 39.938 ], [ 5.718, - 26.852 + 39.957 ], [ 5.851, - 27.013 + 40.118 ], [ 6.092, - 27.016 + 40.12 ], [ 6.553, - 26.688 + 39.793 ], [ 6.541, - 26.126 + 39.233 ], [ 6.396, - 26.054 + 39.16 ], [ 6.397, - 25.365 + 38.473 ], [ 6.227, - 25.11 + 38.218 ], [ 6.492, - 24.819 + 37.927 ], [ - 6.482, - 24.656 + 6.481, + 37.765 ], [ 6.748, - 24.656 + 37.765 ], [ 6.965, - 24.331 + 37.44 ], [ 7.594, - 24.111 + 37.22 ], [ - 7.56, - 24.493 + 7.559, + 37.602 ], [ 7.316, - 24.891 + 37.999 ], [ 7.292, - 25.473 + 38.58 ], [ 6.979, - 25.98 + 39.086 ], [ 7.135, - 26.291 + 39.397 ], [ - 7.451, - 26.255 + 7.45, + 39.361 ], [ 7.473, - 26.651 + 39.757 ], [ 7.909, - 26.434 + 39.539 ], [ 7.824, - 26.272 + 39.378 ], [ 7.91, - 26.106 + 39.212 ], [ 8.478, - 26.524 + 39.63 ], [ 8.273, - 26.835 + 39.94 ], [ - 9.267, - 26.616 + 9.266, + 39.722 ], [ 9.606, - 26.399 + 39.504 ], [ 9.679, - 26.181 + 39.287 ], [ 9.921, - 26.362 + 39.468 ], [ 10.962, - 26.288 + 39.395 ], [ 10.925, - 26.561 + 39.666 ], [ - 11.146, - 26.78 + 11.145, + 39.885 ], [ 11.107, - 26.943 + 40.048 ], [ 11.666, - 27.087 + 40.192 ], [ 12.514, - 26.904 + 40.009 ], [ - 12.878, - 26.615 + 12.877, + 39.721 ], [ 13.288, - 26.615 + 39.721 ], [ 14.016, - 26.107 + 39.213 ], [ - 15.105, - 26.181 + 15.104, + 39.287 ], [ 15.384, - 26.38 + 39.486 ], [ 15.359, - 26.67 + 39.775 ], [ 14.863, - 26.471 + 39.577 ], [ - 14.803, - 27.031 + 14.802, + 40.136 ], [ 14.705, - 27.105 + 40.21 ], [ 14.983, - 27.123 + 40.228 ], [ 15.65, - 27.578 + 40.682 ], [ 15.276, - 27.813 + 40.916 ], [ 15.685, - 27.741 + 40.845 ], [ 15.952, - 28.103 + 41.206 ], [ 16.219, - 28.141 + 41.244 ], [ 16.437, - 28.102 + 41.205 ], [ - 16.657, - 27.813 + 16.656, + 40.916 ], [ 17.044, - 28.575 + 41.677 ], [ 17.019, - 28.247 + 41.35 ], [ 17.769, - 28.357 + 41.46 ], [ 17.841, - 28.504 + 41.606 ], [ 17.916, - 28.357 + 41.46 ], [ 18.545, - 28.358 + 41.461 ], [ 18.605, - 28.23 + 41.333 ], [ 17.09, - 28.175 + 41.278 ], [ 16.947, - 27.778 + 40.881 ], [ 16.751, - 27.669 + 40.773 ], [ 16.17, - 28.032 + 41.135 ], [ 15.759, - 27.414 + 40.518 ], [ 14.983, - 26.977 + 40.082 ], [ 14.959, - 26.723 + 39.828 ], [ 15.443, - 26.907 + 40.012 ], [ 15.904, - 26.653 + 39.758 ], [ 16.085, - 26.451 + 39.556 ], [ 16.049, - 26.287 + 39.393 ], [ 16.607, - 26.435 + 39.541 ], [ 16.728, - 26.251 + 39.357 ], [ 16.922, - 26.397 + 39.503 ], [ 17.067, - 26.216 + 39.322 ], [ - 17.735, - 26.126 + 17.734, + 39.233 ], [ 16.487, - 26.106 + 39.212 ], [ 16.243, - 25.998 + 39.105 ], [ 16.062, - 25.69 + 38.797 ], [ 16.438, - 25.49 + 38.597 ], [ 16.51, - 25.381 + 38.489 ], [ 16.05, - 25.563 + 38.67 ], [ 15.928, - 25.998 + 39.105 ], [ 15.493, - 25.6 + 38.707 ], [ 14.923, - 25.438 + 38.545 ], [ 15.056, - 25.198 + 38.306 ], [ - 15.663, - 24.982 + 15.662, + 38.09 ], [ 15.856, - 24.8 + 37.909 ], [ 15.179, - 25.127 + 38.235 ], [ 14.862, - 25.089 + 38.197 ], [ 14.67, - 25.489 + 38.596 ], [ 13.3, - 25.29 + 38.398 ], [ 13.652, - 24.947 + 38.055 ], [ 13.845, - 25.018 + 38.126 ], [ 13.688, - 24.856 + 37.964 ], [ 13.773, - 24.728 + 37.836 ], [ 14.136, - 24.729 + 37.837 ], [ 14.269, - 24.637 + 37.746 ], [ 13.869, - 24.691 + 37.8 ], [ 13.845, - 24.437 + 37.546 ], [ 14.79, - 24.148 + 37.258 ], [ 14.946, - 24.021 + 37.131 ], [ 14.621, - 23.892 + 37.002 ], [ - 14.644, - 23.785 + 14.643, + 36.895 ], [ 15.251, - 23.713 + 36.823 ], [ 15.347, - 23.529 + 36.639 ], [ 14.96, - 23.64 + 36.75 ], [ 14.789, - 23.531 + 36.642 ], [ 14.692, - 23.711 + 36.822 ], [ 14.498, - 23.566 + 36.677 ], [ - 14.283, - 23.639 + 14.282, + 36.749 ], [ 14.197, - 23.875 + 36.985 ], [ 14.366, - 23.984 + 37.093 ], [ 14.258, - 24.147 + 37.256 ], [ 13.532, - 24.291 + 37.4 ], [ 13.469, - 23.912 + 37.022 ], [ 13.772, - 23.384 + 36.494 ], [ 13.095, - 24.11 + 37.219 ], [ 12.803, - 24.111 + 37.22 ], [ - 12.815, - 23.911 + 12.814, + 37.021 ], [ 13.022, - 23.82 + 36.93 ], [ 13.167, - 23.529 + 36.639 ], [ 12.562, - 24.003 + 37.113 ], [ 12.33, - 24.385 + 37.494 ], [ 12.405, - 24.566 + 37.674 ], [ 12.332, - 24.783 + 37.892 ], [ 12.175, - 24.91 + 38.019 ], [ 11.024, - 24.891 + 37.999 ], [ 11.277, - 24.436 + 37.545 ], [ 11.641, - 24.183 + 37.293 ], [ 10.283, - 24.835 + 37.944 ], [ - 9.909, - 24.783 + 9.908, + 37.892 ], [ 10.054, - 24.673 + 37.782 ], [ 10.064, - 24.33 + 37.439 ], [ 10.441, - 24.094 + 37.203 ], [ 10.199, - 23.949 + 37.058 ], [ 10.442, - 23.477 + 36.587 ], [ 10.404, - 23.312 + 36.423 ], [ 10.939, - 22.987 + 36.098 ], [ 11.495, - 22.222 + 35.335 ], [ 12.174, - 21.969 + 35.082 ], [ 12.705, - 21.534 + 34.647 ], [ 13.215, - 21.46 + 34.574 ], [ 14.403, - 20.626 + 33.741 ], [ 14.428, - 20.481 + 33.596 ], [ - 13.071, - 21.316 + 13.07, + 34.43 ], [ 12.632, - 21.245 + 34.359 ], [ 12.368, - 21.645 + 34.758 ], [ 12.173, - 21.643 + 34.756 ], [ - 12.186, - 21.479 + 12.185, + 34.593 ], [ 11.957, - 21.279 + 34.392 ], [ 12.113, - 21.733 + 34.847 ], [ - 11.666, - 21.824 + 11.665, + 34.937 ], [ 11.471, - 22.116 + 35.228 ], [ - 10.89, - 21.823 + 10.889, + 34.936 ], [ 10.551, - 21.859 + 34.972 ], [ 10.187, - 22.514 + 35.626 ], [ 9.993, - 22.405 + 35.517 ], [ 9.775, - 22.514 + 35.626 ], [ 9.836, - 22.388 + 35.5 ], [ - 9.703, - 22.477 + 9.702, + 35.59 ], [ 9.765, - 22.315 + 35.428 ], [ 9.654, - 22.406 + 35.518 ], [ 9.678, - 22.262 + 35.375 ], [ 9.34, - 22.551 + 35.663 ], [ 9.048, - 22.44 + 35.552 ], [ - 9.024, - 22.66 + 9.023, + 35.772 ], [ 8.711, - 22.514 + 35.626 ], [ 8.661, - 22.258 + 35.371 ], [ 8.661, - 22.625 + 35.737 ], [ 8.468, - 22.586 + 35.698 ], [ 7.618, - 23.494 + 36.604 ], [ 7.061, - 23.386 + 36.497 ], [ - 6.893, - 23.675 + 6.892, + 36.785 ], [ 6.724, - 23.604 + 36.714 ], [ 6.506, - 23.894 + 37.004 ], [ 6.141, - 23.967 + 37.076 ], [ 5.389, - 24.475 + 37.584 ], [ 5.247, - 24.439 + 37.548 ], [ 5.22, - 24.257 + 37.366 ], [ 4.834, - 24.365 + 37.474 ], [ 4.52, - 24.003 + 37.113 ], [ 4.252, - 24.077 + 37.186 ], [ 4.035, - 23.819 + 36.929 ], [ 3.355, - 23.603 + 36.713 ], [ 2.775, - 23.857 + 36.967 ], [ 2.714, - 24.02 + 37.13 ], [ 2.811, - 24.347 + 37.456 ], [ 2.507, - 24.691 + 37.8 ], [ 1.66, - 24.581 + 37.69 ], [ 1.2, - 24.8 + 37.909 ], [ 0.667, - 24.691 + 37.8 ], [ 0.581, - 24.347 + 37.456 ], [ - 0.728, - 23.982 + 0.727, + 37.092 ], [ 0.363, - 23.658 + 36.768 ], [ 0.425, - 23.386 + 36.497 ], [ 0.799, - 23.332 + 36.442 ], [ 0.231, - 23.312 + 36.423 ], [ - 0.17, - 23.04 + 0.169, + 36.151 ], [ 0.715, - 23.132 + 36.243 ], [ - 0.971, - 22.857 + 0.97, + 35.969 ], [ 0.97, - 22.604 + 35.716 ], [ 0.824, - 22.461 + 35.573 ], [ 0.922, - 22.385 + 35.498 ], [ 0.667, - 22.332 + 35.445 ], [ 0.558, - 22.061 + 35.174 ], [ 0.605, - 21.481 + 34.594 ], [ 0.279, - 21.426 + 34.54 ], [ 0.256, - 21.172 + 34.286 ], [ - 0.0, - 21.006 + -0.0, + 34.121 ], [ 0.388, - 20.355 + 33.471 ], [ 0.266, - 20.103 + 33.218 ], [ 0.533, - 19.703 + 32.82 ], [ 0.34, - 19.665 + 32.781 ], [ 0.702, - 19.159 + 32.276 ], [ 0.715, - 18.848 + 31.966 ], [ - 0.837, - 18.884 + 0.836, + 32.002 ], [ 1.005, - 18.631 + 31.75 ], [ 1.162, - 18.723 + 31.841 ], [ 1.103, - 18.958 + 32.076 ], [ - 1.248, - 18.521 + 1.247, + 31.64 ], [ - 1.818, - 18.432 + 1.817, + 31.55 ], [ 1.272, - 18.377 + 31.496 ], [ 0.981, - 18.558 + 31.676 ], [ 0.677, - 18.177 + 31.295 ], [ 0.716, - 18.014 + 31.133 ], [ - 0.535, - 17.924 + 0.534, + 31.043 ], [ 0.677, - 17.851 + 30.97 ], [ 0.484, - 17.414 + 30.534 ], [ 0.582, - 17.199 + 30.319 ], [ 0.508, - 17.051 + 30.172 ], [ 0.8, - 16.835 + 29.956 ], [ 0.752, - 16.546 + 29.667 ], [ - 0.971, - 16.363 + 0.97, + 29.485 ], [ 0.872, - 16.325 + 29.447 ], [ 0.995, - 16.144 + 29.266 ], [ 0.872, - 15.925 + 29.047 ], [ - 1.126, - 15.838 + 1.125, + 28.96 ], [ 1.296, - 15.981 + 29.103 ], [ 1.259, - 15.782 + 28.905 ], [ - 1.357, - 15.673 + 1.356, + 28.796 ], [ 1.258, - 15.419 + 28.543 ], [ 1.732, - 15.112 + 28.236 ], [ 1.926, - 14.747 + 27.871 ], [ 2.266, - 14.712 + 27.836 ], [ 2.264, - 14.893 + 28.017 ], [ 2.483, - 14.819 + 27.943 ], [ 2.581, - 15.038 + 28.162 ], [ 2.724, - 14.893 + 28.017 ], [ 2.592, - 14.764 + 27.888 ], [ 2.689, - 14.403 + 27.528 ], [ 2.581, - 14.167 + 27.292 ], [ 2.943, - 14.274 + 27.399 ], [ 3.185, - 14.132 + 27.257 ], [ 3.175, - 14.328 + 27.453 ], [ 3.284, - 14.382 + 27.507 ], [ 3.514, - 14.259 + 27.384 ], [ 3.574, - 14.021 + 27.146 ], [ 4.131, - 14.203 + 27.329 ], [ 4.81, - 13.912 + 27.037 ], [ 5.755, - 14.203 + 27.329 ], [ 5.973, - 14.818 + 27.942 ], [ 6.383, - 14.71 + 27.835 ], [ 6.02, - 14.529 + 27.654 ], [ 5.875, - 14.095 + 27.22 ], [ 5.306, - 13.931 + 27.057 ], [ - 5.572, - 13.64 + 5.571, + 26.766 ], [ 5.559, - 13.476 + 26.603 ], [ 5.404, - 13.459 + 26.586 ], [ 5.525, - 13.278 + 26.404 ], [ 5.452, - 13.023 + 26.15 ], [ 5.706, - 12.606 + 25.734 ], [ 6.058, - 12.914 + 26.041 ], [ 5.851, - 13.186 + 26.313 ], [ 7.329, - 13.005 + 26.132 ], [ 7.838, - 13.26 + 26.386 ], [ 7.231, - 12.824 + 25.952 ], [ 6.869, - 12.787 + 25.914 ], [ 6.964, - 12.424 + 25.552 ], [ - 6.566, - 12.515 + 6.565, + 25.642 ], [ 6.663, - 12.262 + 25.39 ], [ 6.493, - 12.153 + 25.281 ], [ 6.638, - 12.043 + 25.171 ], [ 6.589, - 11.825 + 24.954 ], [ 7.306, - 11.843 + 24.972 ], [ 7.147, - 11.428 + 24.558 ], [ 7.376, - 11.261 + 24.391 ], [ 7.485, - 11.317 + 24.446 ], [ 7.39, - 11.535 + 24.664 ], [ 7.547, - 11.624 + 24.753 ], [ 8.079, - 11.409 + 24.538 ], [ 8.237, - 11.571 + 24.7 ], [ - 8.164, - 11.789 + 8.163, + 24.918 ], [ - 8.273, - 11.953 + 8.272, + 25.082 ], [ 9.606, - 11.733 + 24.862 ], [ 10.164, - 11.19 + 24.32 ], [ 10.332, - 11.19 + 24.32 ], [ 10.405, - 11.479 + 24.608 ], [ 10.6, - 11.155 + 24.285 ], [ 11.253, - 11.261 + 24.391 ], [ 10.526, - 10.862 + 23.992 ], [ 10.417, - 10.518 + 23.649 ], [ 11.638, - 10.464 + 23.595 ], [ 12.174, - 9.956 + 23.088 ], [ 13.046, - 9.52 + 22.653 ], [ 13.553, - 9.665 + 22.798 ], [ 13.495, - 9.938 + 23.069 ], [ 13.602, - 10.028 + 23.16 ], [ 14.014, - 9.774 + 22.906 ], [ 14.136, - 10.028 + 23.16 ], [ - 14.451, - 10.101 + 14.45, + 23.232 ], [ 14.512, - 10.301 + 23.432 ], [ - 14.367, - 10.446 + 14.366, + 23.577 ], [ 14.389, - 10.63 + 23.76 ], [ 13.859, - 10.954 + 24.084 ], [ 14.101, - 11.028 + 24.158 ], [ - 13.931, - 11.207 + 13.93, + 24.337 ], [ 13.99, - 11.335 + 24.465 ], [ - 14.329, - 11.481 + 14.328, + 24.611 ], [ - 14.535, - 11.353 + 14.534, + 24.483 ], [ 14.401, - 11.189 + 24.318 ], [ 14.863, - 11.081 + 24.211 ], [ 15.128, - 10.754 + 23.885 ], [ - 15.881, - 10.754 + 15.88, + 23.885 ], [ 16.46, - 10.538 + 23.669 ], [ 16.584, - 10.535 + 23.666 ], [ 16.425, - 10.662 + 23.793 ], [ 16.63, - 10.792 + 23.922 ], [ 17.357, - 10.645 + 23.776 ], [ 16.644, - 10.373 + 23.504 ], [ 16.896, - 9.884 + 23.016 ], [ 17.744, - 9.122 + 22.255 ], [ 17.892, - 9.229 + 22.362 ], [ - 19.224, - 8.758 + 19.223, + 21.892 ], [ - 20.046, - 8.868 + 20.045, + 22.002 ], [ - 20.193, - 8.757 + 20.192, + 21.891 ], [ 20.349, - 8.849 + 21.982 ], [ 20.335, - 9.157 + 22.29 ], [ 20.482, - 8.902 + 22.035 ], [ 20.578, - 9.122 + 22.255 ], [ 20.748, - 8.866 + 21.999 ], [ 20.784, - 9.139 + 22.272 ], [ 21.136, - 9.304 + 22.436 ], [ 21.086, - 9.449 + 22.581 ], [ 21.354, - 9.667 + 22.799 ], [ 21.076, - 9.174 + 22.307 ], [ 21.56, - 9.175 + 22.308 ], [ 21.367, - 9.031 + 22.165 ], [ 21.51, - 8.485 + 21.619 ], [ 21.936, - 8.394 + 21.528 ], [ 22.033, - 7.997 + 21.132 ], [ 22.616, - 8.032 + 21.167 ], [ 22.93, - 7.344 + 20.48 ], [ 23.061, - 7.396 + 20.532 ], [ 22.917, - 7.687 + 20.823 ], [ 22.991, - 7.943 + 21.078 ], [ 23.208, - 8.076 + 21.21 ], [ - 23.414, - 7.876 + 23.413, + 21.011 ], [ 23.404, - 7.492 + 20.628 ], [ 23.741, - 7.421 + 20.557 ], [ 23.656, - 7.584 + 20.72 ], [ 23.815, - 7.639 + 20.774 ], [ 24.021, - 7.476 + 20.611 ], [ - 24.093, - 7.802 + 24.092, + 20.937 ], [ 23.874, - 8.708 + 21.842 ], [ 24.359, - 9.18 + 22.313 ], [ 24.067, - 9.109 + 22.242 ], [ 24.093, - 9.616 + 22.748 ], [ 23.971, - 9.797 + 22.929 ], [ 24.516, - 9.708 + 22.84 ], [ 24.6, - 9.871 + 23.003 ], [ 24.48, - 10.015 + 23.147 ], [ 24.577, - 10.379 + 23.51 ], [ 24.467, - 10.978 + 24.108 ], [ 24.832, - 10.361 + 23.492 ], [ 25.122, - 10.47 + 23.601 ], [ - 25.389, - 9.708 + 25.388, + 22.84 ], [ 25.775, - 9.962 + 23.094 ], [ - 26.115, - 9.815 + 26.114, + 22.947 ], [ 26.345, - 10.053 + 23.184 ], [ 25.91, - 10.305 + 23.437 ], [ 26.005, - 10.706 + 23.836 ], [ 26.26, - 11.088 + 24.218 ], [ 27.23, - 11.558 + 24.687 ], [ 27.351, - 12.066 + 25.194 ], [ 27.631, - 11.76 + 24.889 ], [ 27.073, - 9.835 + 22.967 ], [ 27.325, - 9.779 + 22.911 ], [ 27.388, - 10.234 + 23.365 ], [ - 27.57, - 10.359 + 27.569, + 23.49 ], [ 27.532, - 9.434 + 22.567 ], [ 27.314, - 9.255 + 22.388 ], [ 27.495, - 8.872 + 22.005 ], [ 27.775, - 8.71 + 21.843 ], [ - 27.738, - 8.327 + 27.737, + 21.462 ], [ 27.884, - 8.328 + 21.463 ], [ 28.052, - 8.037 + 21.172 ], [ - 28.187, - 8.491 + 28.186, + 21.625 ], [ 28.32, - 8.545 + 21.679 ], [ 28.479, - 8.165 + 21.3 ], [ 28.392, - 7.857 + 20.992 ], [ - 28.732, - 8.11 + 28.731, + 21.244 ], [ - 28.975, - 8.037 + 28.974, + 21.172 ], [ - 29.336, - 8.546 + 29.335, + 21.68 ], [ 30.161, - 8.22 + 21.354 ], [ 31.228, - 8.364 + 21.498 ], [ 31.36, - 8.454 + 21.588 ], [ - 31.324, - 8.619 + 31.323, + 21.753 ], [ 31.809, - 9.017 + 22.15 ], [ 32.245, - 8.981 + 22.114 ], [ 32.413, - 9.198 + 22.331 ], [ 33.091, - 8.765 + 21.899 ], [ 33.333, - 8.907 + 22.04 ], [ 33.479, - 8.764 + 21.898 ], [ 33.177, - 9.398 + 22.531 ], [ 33.588, - 9.798 + 22.93 ], [ 33.238, - 10.107 + 23.239 ], [ 33.672, - 9.924 + 23.056 ], [ 33.661, - 9.434 + 22.567 ], [ 33.988, - 9.235 + 22.368 ], [ 34.302, - 9.307 + 22.44 ], [ 34.472, - 9.853 + 22.985 ], [ 34.861, - 9.962 + 23.094 ], [ 35.417, - 9.744 + 22.876 ], [ 35.672, - 9.364 + 22.497 ], [ 35.235, - 9.036 + 22.17 ], [ 35.249, - 7.494 + 20.629 ], [ 35.467, - 7.604 + 20.739 ], [ 35.755, - 7.422 + 20.558 ], [ 35.781, - 7.238 + 20.375 ], [ 36.242, - 7.421 + 20.557 ], [ 36.58, - 7.384 + 20.52 ], [ 36.557, - 7.202 + 20.338 ], [ 37.028, - 7.222 + 20.358 ], [ 36.218, - 7.168 + 20.305 ], [ - 36.206, - 6.568 + 36.205, + 19.705 ], [ 35.78, - 6.042 + 19.18 ], [ 35.2, - 6.151 + 19.289 ], [ 35.356, - 5.806 + 18.944 ], [ 35.09, - 5.623 + 18.762 ], [ 35.236, - 5.516 + 18.654 ], [ 35.21, - 5.153 + 18.292 ], [ 35.38, - 5.006 + 18.146 ], [ 35.187, - 4.755 + 17.895 ], [ 35.611, - 4.699 + 17.839 ], [ 35.549, - 4.536 + 17.676 ], [ 35.709, - 4.372 + 17.513 ], [ 35.998, - 4.626 + 17.767 ], [ - 36.411, - 4.41 + 36.41, + 17.55 ], [ - 36.617, - 4.028 + 36.616, + 17.169 ], [ 36.555, - 3.827 + 16.968 ], [ 36.314, - 3.865 + 17.007 ], [ - 36.277, - 3.627 + 36.276, + 16.769 ], [ 36.495, - 3.556 + 16.698 ], [ - 36.508, - 3.21 + 36.507, + 16.352 ], [ 37.113, - 3.464 + 16.606 ], [ 36.906, - 3.01 + 16.153 ], [ 37.281, - 2.702 + 15.845 ], [ - 37.464, - 2.902 + 37.463, + 16.045 ], [ 37.392, - 3.046 + 16.189 ], [ 37.599, - 3.21 + 16.352 ], [ 37.623, - 2.918 + 16.061 ], [ 38.059, - 3.137 + 16.28 ], [ 37.95, - 2.939 + 16.082 ], [ - 38.022, - 2.757 + 38.021, + 15.9 ], [ 38.3, - 2.665 + 15.809 ], [ 38.529, - 3.194 + 16.336 ], [ 38.93, - 3.609 + 16.751 ], [ - 39.779, - 3.427 + 39.778, + 16.57 ], [ 39.837, - 3.554 + 16.696 ], [ 39.584, - 3.755 + 16.897 ], [ 39.438, - 4.153 + 17.294 ], [ 39.33, - 4.138 + 17.279 ], [ 39.367, - 4.407 + 17.548 ], [ 40.77, - 4.226 + 17.367 ], [ 40.903, - 4.099 + 17.24 ], [ 40.868, - 3.609 + 16.751 ], [ 41.014, - 3.683 + 16.824 ], [ 41.691, - 3.356 + 16.498 ], [ 41.959, - 3.429 + 16.571 ], [ 42.83, - 2.591 + 15.735 ], [ 43.485, - 2.739 + 15.882 ], [ 43.702, - 2.957 + 16.1 ], [ - 43.969, - 2.414 + 43.968, + 15.557 ], [ 44.185, - 2.375 + 15.519 ], [ 44.186, - 2.629 + 15.772 ], [ 44.478, - 2.774 + 15.917 ], [ 44.732, - 2.431 + 15.574 ], [ 44.743, - 2.157 + 15.301 ], [ 45.312, - 1.523 + 14.668 ], [ 45.253, - 1.359 + 14.504 ], [ 45.641, - 1.213 + 14.359 ], [ 45.845, - 1.414 + 14.56 ], [ 46.162, - 2.213 + 15.357 ], [ 45.799, - 2.721 + 15.864 ], [ 46.307, - 2.685 + 15.828 ], [ 46.052, - 4.008 + 17.149 ], [ 46.377, - 3.63 + 16.771 ], [ 46.404, - 3.192 + 16.334 ], [ 46.621, - 2.646 + 15.789 ], [ 46.877, - 2.375 + 15.519 ], [ 47.046, - 2.376 + 15.52 ], [ 47.094, - 2.63 + 15.774 ], [ 47.286, - 1.978 + 15.123 ], [ 47.772, - 1.977 + 15.121 ], [ 48.305, - 1.723 + 14.868 ], [ 48.45, - 1.431 + 14.577 ], [ 49.031, - 1.357 + 14.503 ], [ - 49.153, - 1.141 + 49.152, + 14.287 ], [ 49.444, - 1.178 + 14.324 ], [ 50.025, - 0.633 + 13.779 ], [ - 50.109, - 0.69 + 50.108, + 13.836 ], [ 49.953, - 0.961 + 14.107 ], [ 49.333, - 1.377 + 14.522 ], [ 49.759, - 1.287 + 14.433 ], [ 49.963, - 1.705 + 14.85 ], [ 49.88, - 1.832 + 14.976 ], [ 49.686, - 1.579 + 14.724 ], [ - 49.455, - 1.668 + 49.454, + 14.813 ], [ - 49.627, - 1.812 + 49.626, + 14.957 ], [ 49.745, - 2.285 + 15.429 ], [ 49.358, - 2.865 + 16.008 ], [ 49.638, - 2.81 + 15.954 ], [ 49.891, - 3.011 + 16.154 ], [ 49.746, - 3.119 + 16.262 ], [ 49.648, - 3.738 + 16.88 ], [ 49.287, - 4.099 + 17.24 ], [ 49.455, - 4.099 + 17.24 ], [ 49.212, - 4.753 + 17.893 ], [ 49.333, - 4.97 + 18.11 ], [ 48.558, - 5.516 + 18.654 ], [ 48.656, - 5.588 + 18.727 ], [ 48.584, - 6.095 + 19.233 ], [ 48.328, - 6.442 + 19.58 ], [ 48.692, - 6.222 + 19.36 ], [ 48.815, - 5.824 + 18.962 ], [ 49.334, - 5.625 + 18.763 ], [ 49.165, - 5.151 + 18.291 ], [ - 49.455, - 5.041 + 49.454, + 18.181 ], [ - 49.627, - 3.955 + 49.626, + 17.096 ], [ 50.048, - 3.391 + 16.533 ], [ 50.631, - 3.282 + 16.425 ], [ 50.546, - 3.483 + 16.625 ], [ - 50.705, - 3.573 + 50.704, + 16.715 ], [ 51.14, - 3.356 + 16.498 ], [ - 51.816, - 3.319 + 51.815, + 16.461 ], [ 51.952, - 3.483 + 16.625 ], [ 51.708, - 3.773 + 16.915 ], [ 52.034, - 4.044 + 17.186 ], [ 52.047, - 4.244 + 17.385 ], [ 51.673, - 4.371 + 17.512 ], [ 51.454, - 4.699 + 17.839 ], [ 51.042, - 4.734 + 17.874 ], [ 50.909, - 4.936 + 18.076 ], [ 51.914, - 5.063 + 18.203 ], [ 51.394, - 5.259 + 18.398 ], [ 51.841, - 5.714 + 18.853 ], [ - 52.013, - 5.643 + 52.012, + 18.781 ], [ 52.254, - 5.824 + 18.962 ], [ 53.246, - 5.897 + 19.035 ], [ 53.658, - 5.461 + 18.6 ], [ 53.573, - 5.696 + 18.834 ], [ 53.826, - 6.076 + 19.214 ], [ 54.253, - 6.203 + 19.341 ], [ 53.913, - 6.678 + 19.815 ], [ 53.924, - 6.875 + 20.012 ], [ 54.132, - 7.039 + 20.175 ], [ 53.72, - 7.477 + 20.613 ], [ 53.767, - 7.693 + 20.829 ], [ 54.217, - 7.711 + 20.847 ], [ 54.179, - 7.91 + 21.045 ], [ 54.507, - 8.401 + 21.535 ], [ 54.99, - 8.655 + 21.789 ], [ 55.33, - 8.618 + 21.752 ], [ 55.537, - 8.964 + 22.097 ], [ 55.546, - 9.636 + 22.769 ], [ 56.08, - 9.815 + 22.947 ], [ 56.274, - 9.706 + 22.839 ], [ 56.784, - 10.143 + 23.275 ], [ 56.856, - 10.47 + 23.601 ], [ 57.184, - 10.597 + 23.728 ], [ 57.256, - 10.995 + 24.125 ], [ 57.401, - 11.105 + 24.235 ], [ 57.302, - 11.358 + 24.488 ], [ 57.473, - 11.506 + 24.635 ], [ 57.402, - 12.012 + 25.141 ], [ 57.619, - 12.337 + 25.465 ], [ 57.472, - 12.775 + 25.902 ], [ 57.642, - 12.92 + 26.047 ], [ 57.341, - 13.011 + 26.138 ], [ 57.414, - 13.23 + 26.356 ], [ 58.041, - 13.411 + 26.537 ], [ 58.189, - 13.628 + 26.754 ], [ - 58.527, - 13.591 + 58.526, + 26.717 ], [ 58.734, - 13.825 + 26.95 ], [ 58.77, - 14.171 + 27.296 ], [ - 59.11, - 14.426 + 59.109, + 27.551 ], [ 60.536, - 14.897 + 28.021 ], [ 60.514, - 15.078 + 28.202 ], [ 60.235, - 15.241 + 28.365 ], [ 60.429, - 15.715 + 28.837 ], [ 60.139, - 16.039 + 29.161 ], [ 60.49, - 16.023 + 29.145 ], [ 60.526, - 16.477 + 29.598 ], [ 60.805, - 16.566 + 29.688 ], [ - 61.362, - 17.512 + 61.361, + 30.632 ], [ 61.617, - 16.767 + 29.888 ], [ 61.448, - 16.005 + 29.127 ], [ 61.506, - 15.84 + 28.963 ], [ 61.821, - 15.733 + 28.855 ], [ 62.234, - 16.24 + 29.361 ], [ 62.887, - 16.204 + 29.326 ], [ 63.034, - 16.35 + 29.471 ], [ 62.85, - 16.04 + 29.162 ], [ 62.899, - 15.715 + 28.837 ], [ - 63.736, - 15.042 + 63.735, + 28.166 ], [ 64.654, - 13.918 + 27.043 ], [ 64.802, - 13.953 + 27.079 ], [ 64.716, - 13.753 + 26.879 ], [ - 65.25, - 13.246 + 65.249, + 26.373 ], [ - 65.191, - 13.009 + 65.19, + 26.136 ], [ 65.771, - 13.083 + 26.21 ], [ 66.256, - 12.902 + 26.029 ], [ 67.514, - 11.994 + 25.123 ], [ 67.478, - 11.648 + 24.777 ], [ 67.658, - 11.704 + 24.833 ], [ 67.72, - 11.541 + 24.67 ], [ 67.297, - 11.378 + 24.508 ], [ 67.091, - 11.104 + 24.234 ], [ 67.127, - 10.977 + 24.107 ], [ 67.443, - 11.014 + 24.144 ], [ 67.66, - 10.797 + 23.927 ], [ 68.168, - 10.869 + 24.0 ], [ 68.605, - 10.651 + 23.782 ], [ 69.125, - 10.886 + 24.016 ], [ - 69.029, - 11.032 + 69.028, + 24.163 ], [ 69.16, - 11.486 + 24.616 ], [ 69.345, - 11.32 + 24.45 ], [ 69.113, - 11.159 + 24.289 ], [ 69.791, - 11.051 + 24.181 ], [ 70.082, - 10.615 + 23.746 ], [ 70.35, - 10.65 + 23.781 ], [ - 70.568, - 10.397 + 70.567, + 23.528 ], [ 70.81, - 10.541 + 23.672 ], [ 71.366, - 9.962 + 23.094 ], [ 71.754, - 9.78 + 22.912 ], [ 71.813, - 9.325 + 22.458 ], [ 72.046, - 9.054 + 22.188 ], [ 72.299, - 9.398 + 22.531 ], [ 72.153, - 9.725 + 22.857 ], [ 72.215, - 9.852 + 22.984 ], [ 73.002, - 10.054 + 23.185 ], [ 72.324, - 11.068 + 24.198 ], [ 72.372, - 11.212 + 24.341 ], [ 72.237, - 11.487 + 24.617 ], [ 71.536, - 12.103 + 25.232 ], [ 71.973, - 12.139 + 25.268 ], [ - 72.191, - 12.464 + 72.19, + 25.592 ], [ 72.625, - 11.812 + 24.941 ], [ 72.76, - 11.83 + 24.959 ], [ 72.771, - 12.029 + 25.158 ], [ 72.866, - 11.704 + 24.833 ], [ 73.377, - 11.303 + 24.433 ], [ - 73.353, - 11.158 + 73.352, + 24.288 ], [ 74.151, - 11.012 + 24.142 ], [ 73.921, - 11.468 + 24.597 ], [ 74.335, - 11.902 + 25.031 ], [ 74.164, - 12.266 + 25.395 ], [ 74.637, - 12.465 + 25.593 ], [ 74.794, - 12.81 + 25.937 ], [ 74.768, - 13.173 + 26.299 ], [ - 75.085, - 13.283 + 75.084, + 26.409 ], [ 75.182, - 13.536 + 26.662 ], [ 75.121, - 14.134 + 27.26 ], [ 75.691, - 13.244 + 26.371 ], [ 75.654, - 12.502 + 25.63 ], [ 75.98, - 12.882 + 26.01 ], [ 75.931, - 13.282 + 26.408 ], [ 76.03, - 13.39 + 26.517 ], [ 75.883, - 13.463 + 26.589 ], [ 75.883, - 13.682 + 26.808 ], [ 76.019, - 13.954 + 27.08 ], [ 76.477, - 13.88 + 27.006 ], [ - 76.503, - 13.664 + 76.502, + 26.79 ], [ 76.732, - 13.537 + 26.663 ], [ 76.223, - 13.101 + 26.228 ], [ 76.527, - 12.611 + 25.739 ], [ 76.745, - 12.611 + 25.739 ], [ 76.889, - 12.176 + 25.304 ], [ 77.035, - 12.139 + 25.268 ], [ 77.178, - 12.502 + 25.63 ], [ 77.664, - 12.794 + 25.921 ], [ 77.471, - 12.429 + 25.557 ], [ 77.241, - 12.338 + 25.466 ], [ 77.156, - 11.922 + 25.05 ], [ 76.876, - 11.795 + 24.924 ], [ 76.889, - 11.524 + 24.653 ], [ 77.106, - 11.63 + 24.759 ], [ 77.398, - 11.305 + 24.434 ], [ 77.664, - 11.45 + 24.579 ], [ 77.954, - 11.051 + 24.181 ], [ 78.027, - 11.305 + 24.434 ], [ 78.221, - 11.449 + 24.578 ], [ 78.197, - 11.27 + 24.399 ], [ 78.393, - 11.303 + 24.433 ], [ 78.488, - 11.521 + 24.651 ], [ 79.02, - 11.197 + 24.327 ], [ 79.529, - 11.233 + 24.363 ], [ 79.677, - 11.414 + 24.543 ], [ 79.891, - 11.267 + 24.397 ], [ 79.639, - 11.866 + 24.995 ], [ 80.39, - 11.904 + 25.032 ], [ 80.413, - 12.229 + 25.357 ], [ 80.219, - 12.483 + 25.611 ], [ - 80.391, - 12.629 + 80.39, + 25.757 ], [ 80.316, - 12.776 + 25.903 ], [ 80.595, - 12.719 + 25.846 ], [ - 80.512, - 12.955 + 80.511, + 26.082 ], [ 80.801, - 13.429 + 26.556 ], [ 81.104, - 13.517 + 26.644 ], [ 81.177, - 13.734 + 26.86 ], [ 81.298, - 13.664 + 26.79 ], [ 81.408, - 13.936 + 27.062 ], [ 80.873, - 14.225 + 27.35 ], [ 81.031, - 14.39 + 27.515 ], [ 81.152, - 14.316 + 27.441 ], [ 81.407, - 14.77 + 27.894 ], [ - 81.431, - 15.061 + 81.43, + 28.185 ], [ 81.624, - 15.17 + 28.294 ], [ 81.614, - 15.37 + 28.493 ], [ 81.977, - 15.37 + 28.493 ], [ 82.049, - 15.769 + 28.892 ], [ 82.399, - 15.171 + 28.295 ], [ 82.266, - 14.86 + 27.984 ], [ 81.938, - 14.878 + 28.003 ], [ 82.037, - 14.444 + 27.569 ], [ 81.623, - 13.717 + 26.843 ], [ 81.88, - 13.591 + 26.717 ], [ - 82.341, - 14.028 + 82.34, + 27.153 ], [ 82.569, - 13.936 + 27.062 ], [ 82.304, - 13.716 + 26.842 ], [ 82.339, - 13.373 + 26.5 ], [ 82.026, - 13.444 + 26.57 ], [ 81.612, - 13.227 + 26.354 ], [ 81.275, - 13.372 + 26.499 ], [ 81.142, - 13.174 + 26.301 ], [ 81.291, - 12.92 + 26.047 ], [ 81.289, - 12.856 + 25.983 ], [ 80.885, - 13.047 + 26.174 ], [ 80.946, - 12.665 + 25.792 ], [ 81.19, - 12.377 + 25.505 ], [ 80.862, - 12.395 + 25.523 ], [ 80.754, - 12.229 + 25.357 ], [ - 80.852, - 11.832 + 80.851, + 24.961 ], [ 80.523, - 11.45 + 24.579 ], [ 80.293, - 11.467 + 24.596 ], [ 80.631, - 11.212 + 24.341 ], [ 80.594, - 11.016 + 24.146 ], [ 80.389, - 10.997 + 24.128 ], [ 80.364, - 10.778 + 23.909 ], [ 81.008, - 10.58 + 23.711 ], [ 81.636, - 9.998 + 23.13 ], [ 81.745, - 9.653 + 22.786 ], [ 81.529, - 9.509 + 22.642 ], [ - 81.54, - 9.307 + 81.539, + 22.44 ], [ 81.636, - 9.416 + 22.549 ], [ 82.316, - 9.198 + 22.331 ], [ - 82.341, - 9.417 + 82.34, + 22.55 ], [ 83.019, - 9.307 + 22.44 ], [ - 82.752, - 9.054 + 82.751, + 22.188 ], [ 82.399, - 9.073 + 22.206 ], [ 83.043, - 8.873 + 22.006 ], [ 83.211, - 8.509 + 21.643 ], [ 83.333, - 8.908 + 22.041 ], [ 83.575, - 8.982 + 22.115 ], [ 83.721, - 8.799 + 21.933 ], [ 84.084, - 9.053 + 22.186 ], [ 84.568, - 8.655 + 21.789 ], [ - 84.908, - 8.799 + 84.907, + 21.933 ], [ 85.294, - 8.619 + 21.753 ], [ 85.876, - 8.11 + 21.244 ], [ 85.815, - 8.527 + 21.661 ], [ 85.67, - 8.673 + 21.807 ], [ 85.816, - 8.78 + 21.913 ], [ 85.741, - 9.035 + 22.168 ], [ 86.058, - 9.508 + 22.641 ], [ 85.343, - 10.069 + 23.201 ], [ 85.306, - 10.487 + 23.618 ], [ 85.041, - 10.996 + 24.126 ], [ 85.901, - 12.285 + 25.413 ], [ - 87.961, - 12.829 + 87.96, + 25.956 ], [ 88.032, - 12.43 + 25.558 ], [ 88.178, - 12.684 + 25.811 ], [ 89.729, - 12.646 + 25.774 ], [ - 91.933, - 12.212 + 91.932, + 25.34 ], [ 92.344, - 12.031 + 25.159 ], [ 92.78, - 11.63 + 24.759 ], [ 92.78, - 11.486 + 24.616 ], [ 93.046, - 11.486 + 24.616 ], [ 93.168, - 11.266 + 24.396 ], [ 93.459, - 11.451 + 24.581 ], [ 95.275, - 11.666 + 24.796 ], [ 95.517, - 12.067 + 25.195 ], [ 95.567, - 11.595 + 24.724 ], [ 95.737, - 11.594 + 24.723 ], [ - 95.762, - 11.741 + 95.761, + 24.87 ], [ 96.026, - 11.666 + 24.796 ], [ 96.149, - 11.92 + 25.049 ], [ 96.292, - 11.666 + 24.796 ], [ 96.728, - 11.884 + 25.013 ], [ 96.838, - 11.795 + 24.924 ], [ 96.608, - 11.521 + 24.651 ], [ 96.85, - 11.449 + 24.578 ], [ 97.163, - 11.666 + 24.796 ], [ 97.238, - 11.34 + 24.469 ], [ 97.892, - 11.087 + 24.217 ], [ 99.539, - 11.086 + 24.216 ], [ 100.0, - 11.521 + 24.651 ], [ 99.733, - 10.907 + 24.037 ], [ 99.539, - 10.939 + 24.07 ], [ 99.224, - 10.686 + 23.817 ], [ 98.957, - 10.76 + 23.891 ], [ 98.39, - 10.415 + 23.547 ], [ 98.569, - 9.889 + 23.021 ], [ 99.078, - 9.67 + 22.802 ], [ 97.868, - 10.068 + 23.2 ], [ 97.625, - 10.541 + 23.672 ], [ 97.381, - 10.579 + 23.71 ], [ 97.323, - 9.981 + 23.113 ], [ 97.213, - 9.852 + 22.984 ], [ 95.711, - 9.2 + 22.333 ], [ 94.984, - 7.275 + 20.411 ], [ 94.307, - 7.202 + 20.338 ], [ 93.81, - 6.712 + 19.849 ], [ 93.737, - 4.462 + 17.602 ], [ 93.811, - 4.135 + 17.276 ], [ - 93.665, - 3.919 + 93.664, + 17.06 ], [ 93.64, - 3.338 + 16.48 ], [ 93.411, - 2.921 + 16.063 ], [ 93.073, - 2.738 + 15.881 ], [ 92.756, - 2.738 + 15.881 ], [ 92.805, - 2.919 + 16.062 ], [ 92.501, - 2.757 + 15.9 ], [ 93.421, - 2.03 + 15.174 ], [ 93.714, - 1.05 + 14.196 ], [ - 93.812, - 0.0 + 93.811, + 13.148 + ] + ], + "landRings": [ + [ + [ + -9.992, + 32.642 + ], + [ + -7.992, + 31.175 + ], + [ + -6.915, + 32.025 + ], + [ + -9.009, + 33.239 + ], + [ + -9.361, + 33.15 + ], + [ + -8.622, + 32.515 + ], + [ + -9.992, + 32.642 + ] + ], + [ + [ + 17.528, + 58.189 + ], + [ + 17.868, + 58.007 + ], + [ + 17.951, + 58.751 + ], + [ + 17.528, + 58.189 + ] + ], + [ + [ + 22.494, + 19.392 + ], + [ + 23.354, + 20.12 + ], + [ + 21.876, + 19.773 + ], + [ + 22.494, + 19.392 + ] + ], + [ + [ + 35.225, + 17.658 + ], + [ + 35.756, + 17.078 + ], + [ + 35.913, + 17.35 + ], + [ + 35.225, + 17.658 + ] + ], + [ + [ + 37.344, + 67.612 + ], + [ + 39.803, + 67.776 + ], + [ + 40.177, + 68.481 + ], + [ + 38.18, + 68.391 + ], + [ + 37.344, + 67.612 + ] + ], + [ + [ + 44.646, + 80.239 + ], + [ + 45.155, + 78.826 + ], + [ + 47.674, + 79.806 + ], + [ + 47.989, + 81.146 + ], + [ + 50.595, + 82.252 + ], + [ + 49.588, + 83.537 + ], + [ + 47.384, + 82.487 + ], + [ + 45.785, + 82.883 + ], + [ + 45.143, + 82.215 + ], + [ + 45.434, + 81.417 + ], + [ + 44.646, + 80.239 + ] + ], + [ + [ + 52.386, + 78.627 + ], + [ + 53.029, + 78.537 + ], + [ + 53.961, + 78.59 + ], + [ + 53.636, + 79.369 + ], + [ + 52.386, + 78.627 + ] + ], + [ + [ + 52.279, + 15.882 + ], + [ + 51.768, + 15.194 + ], + [ + 52.41, + 15.321 + ], + [ + 52.279, + 15.882 + ] + ], + [ + [ + 52.569, + 14.65 + ], + [ + 52.302, + 15.195 + ], + [ + 51.805, + 15.029 + ], + [ + 51.891, + 14.831 + ], + [ + 52.569, + 14.65 + ] + ], + [ + [ + 55.499, + 80.674 + ], + [ + 56.225, + 80.603 + ], + [ + 56.285, + 80.802 + ], + [ + 55.62, + 81.365 + ], + [ + 55.499, + 80.674 + ] + ], + [ + [ + 56.298, + 71.363 + ], + [ + 56.442, + 69.478 + ], + [ + 57.329, + 70.038 + ], + [ + 56.614, + 70.166 + ], + [ + 56.298, + 71.363 + ] + ], + [ + [ + 57.801, + 70.529 + ], + [ + 58.165, + 69.369 + ], + [ + 58.783, + 69.786 + ], + [ + 57.801, + 70.529 + ] + ], + [ + [ + 76.634, + 86.853 + ], + [ + 75.982, + 85.583 + ], + [ + 76.331, + 84.044 + ], + [ + 79.458, + 82.994 + ], + [ + 81.393, + 83.503 + ], + [ + 80.994, + 83.265 + ], + [ + 82.316, + 83.466 + ], + [ + 82.582, + 82.974 + ], + [ + 80.789, + 81.942 + ], + [ + 76.019, + 82.705 + ], + [ + 75.484, + 84.08 + ], + [ + 72.675, + 85.566 + ], + [ + 70.008, + 83.501 + ], + [ + 68.944, + 83.755 + ], + [ + 69.137, + 84.515 + ], + [ + 68.215, + 84.371 + ], + [ + 65.396, + 83.011 + ], + [ + 66.133, + 82.558 + ], + [ + 66.982, + 83.357 + ], + [ + 67.601, + 82.866 + ], + [ + 66.775, + 82.578 + ], + [ + 67.466, + 82.268 + ], + [ + 67.321, + 81.98 + ], + [ + 66.328, + 82.56 + ], + [ + 66.559, + 80.837 + ], + [ + 65.079, + 79.351 + ], + [ + 67.733, + 77.341 + ], + [ + 68.739, + 77.721 + ], + [ + 68.036, + 77.142 + ], + [ + 68.423, + 76.705 + ], + [ + 68.072, + 76.218 + ], + [ + 66.921, + 76.126 + ], + [ + 66.836, + 74.115 + ], + [ + 67.66, + 74.441 + ], + [ + 69.452, + 74.26 + ], + [ + 70.106, + 73.789 + ], + [ + 68.411, + 73.969 + ], + [ + 66.738, + 73.248 + ], + [ + 66.92, + 72.901 + ], + [ + 66.837, + 72.773 + ], + [ + 62.875, + 73.772 + ], + [ + 63.493, + 73.536 + ], + [ + 63.458, + 72.937 + ], + [ + 64.147, + 72.703 + ], + [ + 64.427, + 72.286 + ], + [ + 64.571, + 71.85 + ], + [ + 64.536, + 71.723 + ], + [ + 63.76, + 72.703 + ], + [ + 63.349, + 72.087 + ], + [ + 63.324, + 72.594 + ], + [ + 62.596, + 72.521 + ], + [ + 63.24, + 72.792 + ], + [ + 63.105, + 73.247 + ], + [ + 61.482, + 72.774 + ], + [ + 61.931, + 73.191 + ], + [ + 61.434, + 74.189 + ], + [ + 59.906, + 73.718 + ], + [ + 58.188, + 74.625 + ], + [ + 56.712, + 74.116 + ], + [ + 56.227, + 72.812 + ], + [ + 54.712, + 71.959 + ], + [ + 54.894, + 71.182 + ], + [ + 55.79, + 71.108 + ], + [ + 56.517, + 72.302 + ], + [ + 57.293, + 71.76 + ], + [ + 57.207, + 71.995 + ], + [ + 57.28, + 72.284 + ], + [ + 57.341, + 72.342 + ], + [ + 57.364, + 71.47 + ], + [ + 58.091, + 72.269 + ], + [ + 58.333, + 71.688 + ], + [ + 60.005, + 72.086 + ], + [ + 60.38, + 70.221 + ], + [ + 61.252, + 70.038 + ], + [ + 61.349, + 69.134 + ], + [ + 60.803, + 67.92 + ], + [ + 61.131, + 68.88 + ], + [ + 60.622, + 69.096 + ], + [ + 60.661, + 69.984 + ], + [ + 58.636, + 68.735 + ], + [ + 59.969, + 68.987 + ], + [ + 59.254, + 67.593 + ], + [ + 58.866, + 68.318 + ], + [ + 58.116, + 67.521 + ], + [ + 58.553, + 68.679 + ], + [ + 56.324, + 68.644 + ], + [ + 56.227, + 69.768 + ], + [ + 55.476, + 69.44 + ], + [ + 55.355, + 70.385 + ], + [ + 54.844, + 69.946 + ], + [ + 54.554, + 70.384 + ], + [ + 54.579, + 69.514 + ], + [ + 54.361, + 69.876 + ], + [ + 54.226, + 69.312 + ], + [ + 54.071, + 70.203 + ], + [ + 53.695, + 69.206 + ], + [ + 54.106, + 68.445 + ], + [ + 53.475, + 68.408 + ], + [ + 52.93, + 66.905 + ], + [ + 53.598, + 68.99 + ], + [ + 53.185, + 69.315 + ], + [ + 53.575, + 69.64 + ], + [ + 53.307, + 70.074 + ], + [ + 54.349, + 71.345 + ], + [ + 54.094, + 71.616 + ], + [ + 52.956, + 71.433 + ], + [ + 52.485, + 70.437 + ], + [ + 52.738, + 70.095 + ], + [ + 52.289, + 70.039 + ], + [ + 52.568, + 70.999 + ], + [ + 52.01, + 71.072 + ], + [ + 51.236, + 67.957 + ], + [ + 50.884, + 68.3 + ], + [ + 51.393, + 68.769 + ], + [ + 51.647, + 71.001 + ], + [ + 51.089, + 70.312 + ], + [ + 49.418, + 71.181 + ], + [ + 49.091, + 70.689 + ], + [ + 49.528, + 70.401 + ], + [ + 48.523, + 69.659 + ], + [ + 48.46, + 70.691 + ], + [ + 48.873, + 70.618 + ], + [ + 48.486, + 72.974 + ], + [ + 49.781, + 75.31 + ], + [ + 48.075, + 74.677 + ], + [ + 47.542, + 72.757 + ], + [ + 48.293, + 71.923 + ], + [ + 47.856, + 69.134 + ], + [ + 46.416, + 68.608 + ], + [ + 46.161, + 67.863 + ], + [ + 46.246, + 66.795 + ], + [ + 46.839, + 66.922 + ], + [ + 46.184, + 66.633 + ], + [ + 46.925, + 65.707 + ], + [ + 47.602, + 66.108 + ], + [ + 48.109, + 65.347 + ], + [ + 48.872, + 65.435 + ], + [ + 48.643, + 65.021 + ], + [ + 47.347, + 65.364 + ], + [ + 47.965, + 64.475 + ], + [ + 48.572, + 64.983 + ], + [ + 48.595, + 64.113 + ], + [ + 49.238, + 64.42 + ], + [ + 48.573, + 64.076 + ], + [ + 47.749, + 64.403 + ], + [ + 47.555, + 64.802 + ], + [ + 47.579, + 64.332 + ], + [ + 48.317, + 63.878 + ], + [ + 47.88, + 63.769 + ], + [ + 48.389, + 63.189 + ], + [ + 47.965, + 62.955 + ], + [ + 47.698, + 63.931 + ], + [ + 46.67, + 63.188 + ], + [ + 47.491, + 65.002 + ], + [ + 46.536, + 64.767 + ], + [ + 46.706, + 65.67 + ], + [ + 46.016, + 65.474 + ], + [ + 46.45, + 65.982 + ], + [ + 45.919, + 66.669 + ], + [ + 46.003, + 67.737 + ], + [ + 44.211, + 66.108 + ], + [ + 41.727, + 65.691 + ], + [ + 41.983, + 65.201 + ], + [ + 44.067, + 65.855 + ], + [ + 43.436, + 64.767 + ], + [ + 41.678, + 64.929 + ], + [ + 42.261, + 64.53 + ], + [ + 42.2, + 63.679 + ], + [ + 42.951, + 63.715 + ], + [ + 44.004, + 62.138 + ], + [ + 44.067, + 61.721 + ], + [ + 42.538, + 63.351 + ], + [ + 42.007, + 62.845 + ], + [ + 41.739, + 64.078 + ], + [ + 40.625, + 63.751 + ], + [ + 41.559, + 64.459 + ], + [ + 41.085, + 65.164 + ], + [ + 39.099, + 65.454 + ], + [ + 36.881, + 62.43 + ], + [ + 36.968, + 61.069 + ], + [ + 36.604, + 62.628 + ], + [ + 33.431, + 61.613 + ], + [ + 32.534, + 61.939 + ], + [ + 31.264, + 60.945 + ], + [ + 32.208, + 60.074 + ], + [ + 32.05, + 59.692 + ], + [ + 30.983, + 60.778 + ], + [ + 30.524, + 59.114 + ], + [ + 30.27, + 60.219 + ], + [ + 30.717, + 61.034 + ], + [ + 27.351, + 61.394 + ], + [ + 27.714, + 60.708 + ], + [ + 26.078, + 59.422 + ], + [ + 26.465, + 58.478 + ], + [ + 25.569, + 58.445 + ], + [ + 26.055, + 58.371 + ], + [ + 25.872, + 57.553 + ], + [ + 25.269, + 57.844 + ], + [ + 24.711, + 56.432 + ], + [ + 24.08, + 56.758 + ], + [ + 23.839, + 56.25 + ], + [ + 23.765, + 56.832 + ], + [ + 23.45, + 56.54 + ], + [ + 23.318, + 56.632 + ], + [ + 23.873, + 56.994 + ], + [ + 23.535, + 57.21 + ], + [ + 23.839, + 58.608 + ], + [ + 22.023, + 58.822 + ], + [ + 23.292, + 58.515 + ], + [ + 21.998, + 58.243 + ], + [ + 21.902, + 58.97 + ], + [ + 20.615, + 58.208 + ], + [ + 21.816, + 58.697 + ], + [ + 20.811, + 57.519 + ], + [ + 20.349, + 58.244 + ], + [ + 18.897, + 55.996 + ], + [ + 18.896, + 56.65 + ], + [ + 18.037, + 56.487 + ], + [ + 19.091, + 56.758 + ], + [ + 19.877, + 58.154 + ], + [ + 18.193, + 58.969 + ], + [ + 18.641, + 58.226 + ], + [ + 18.267, + 58.46 + ], + [ + 18.134, + 57.645 + ], + [ + 17.541, + 57.121 + ], + [ + 17.843, + 57.682 + ], + [ + 17.444, + 58.389 + ], + [ + 17.165, + 58.08 + ], + [ + 18.037, + 59.096 + ], + [ + 17.709, + 59.294 + ], + [ + 18.594, + 59.532 + ], + [ + 17.904, + 60.853 + ], + [ + 17.04, + 60.96 + ], + [ + 16.991, + 60.47 + ], + [ + 16.911, + 60.417 + ], + [ + 16.79, + 61.252 + ], + [ + 12.236, + 61.071 + ], + [ + 12.076, + 59.999 + ], + [ + 13.07, + 59.421 + ], + [ + 12.827, + 57.862 + ], + [ + 10.466, + 53.497 + ], + [ + 7.547, + 51.753 + ], + [ + 7.643, + 50.991 + ], + [ + 6.626, + 52.115 + ], + [ + 4.76, + 50.953 + ], + [ + 1.673, + 50.43 + ], + [ + 2.641, + 50.213 + ], + [ + 2.363, + 49.286 + ], + [ + 8.902, + 48.383 + ], + [ + 10.915, + 47.44 + ], + [ + 14.355, + 48.201 + ], + [ + 15.239, + 46.335 + ], + [ + 15.069, + 45.357 + ], + [ + 14.487, + 45.138 + ], + [ + 14.608, + 44.485 + ], + [ + 13.895, + 43.309 + ], + [ + 12.272, + 43.162 + ], + [ + 10.622, + 41.824 + ], + [ + 8.588, + 42.076 + ], + [ + 8.722, + 42.674 + ], + [ + 6.991, + 44.83 + ], + [ + 7.05, + 42.963 + ], + [ + 6.445, + 42.059 + ], + [ + 7.22, + 41.153 + ], + [ + 4.908, + 41.063 + ], + [ + 5.161, + 40.21 + ], + [ + 4.774, + 39.885 + ], + [ + 6.553, + 39.793 + ], + [ + 6.227, + 38.218 + ], + [ + 7.594, + 37.22 + ], + [ + 6.979, + 39.086 + ], + [ + 7.473, + 39.757 + ], + [ + 7.91, + 39.212 + ], + [ + 8.273, + 39.94 + ], + [ + 10.962, + 39.395 + ], + [ + 11.666, + 40.192 + ], + [ + 15.104, + 39.287 + ], + [ + 15.359, + 39.775 + ], + [ + 14.863, + 39.577 + ], + [ + 14.705, + 40.21 + ], + [ + 15.65, + 40.682 + ], + [ + 15.276, + 40.916 + ], + [ + 16.219, + 41.244 + ], + [ + 16.656, + 40.916 + ], + [ + 17.044, + 41.677 + ], + [ + 18.605, + 41.333 + ], + [ + 16.751, + 40.773 + ], + [ + 16.17, + 41.135 + ], + [ + 14.983, + 40.082 + ], + [ + 17.734, + 39.233 + ], + [ + 16.062, + 38.797 + ], + [ + 16.438, + 38.597 + ], + [ + 16.51, + 38.489 + ], + [ + 16.05, + 38.67 + ], + [ + 15.928, + 39.105 + ], + [ + 14.923, + 38.545 + ], + [ + 15.056, + 38.306 + ], + [ + 15.662, + 38.09 + ], + [ + 15.856, + 37.909 + ], + [ + 14.862, + 38.197 + ], + [ + 14.67, + 38.596 + ], + [ + 13.3, + 38.398 + ], + [ + 15.347, + 36.639 + ], + [ + 13.532, + 37.4 + ], + [ + 13.772, + 36.494 + ], + [ + 12.803, + 37.22 + ], + [ + 13.167, + 36.639 + ], + [ + 12.175, + 38.019 + ], + [ + 11.024, + 37.999 + ], + [ + 11.641, + 37.293 + ], + [ + 9.908, + 37.892 + ], + [ + 10.404, + 36.423 + ], + [ + 11.495, + 35.335 + ], + [ + 13.215, + 34.574 + ], + [ + 14.428, + 33.596 + ], + [ + 12.368, + 34.758 + ], + [ + 11.957, + 34.392 + ], + [ + 12.113, + 34.847 + ], + [ + 11.471, + 35.228 + ], + [ + 10.551, + 34.972 + ], + [ + 10.187, + 35.626 + ], + [ + 8.661, + 35.371 + ], + [ + 7.618, + 36.604 + ], + [ + 5.389, + 37.584 + ], + [ + 3.355, + 36.713 + ], + [ + 2.507, + 37.8 + ], + [ + 0.667, + 37.8 + ], + [ + 0.363, + 36.768 + ], + [ + 0.799, + 36.442 + ], + [ + 0.169, + 36.151 + ], + [ + 0.97, + 35.716 + ], + [ + -0.0, + 34.121 + ], + [ + 0.715, + 31.966 + ], + [ + 1.817, + 31.55 + ], + [ + 0.981, + 31.676 + ], + [ + 0.534, + 31.043 + ], + [ + 0.872, + 29.047 + ], + [ + 1.975, + 27.835 + ], + [ + 2.606, + 28.161 + ], + [ + 2.581, + 27.292 + ], + [ + 4.81, + 27.037 + ], + [ + 5.973, + 27.942 + ], + [ + 6.383, + 27.835 + ], + [ + 5.306, + 27.057 + ], + [ + 5.452, + 26.15 + ], + [ + 5.706, + 25.734 + ], + [ + 5.851, + 26.313 + ], + [ + 7.838, + 26.386 + ], + [ + 6.565, + 25.642 + ], + [ + 6.589, + 24.954 + ], + [ + 7.306, + 24.972 + ], + [ + 7.376, + 24.391 + ], + [ + 8.272, + 25.082 + ], + [ + 11.253, + 24.391 + ], + [ + 10.417, + 23.649 + ], + [ + 13.046, + 22.653 + ], + [ + 14.45, + 23.232 + ], + [ + 13.859, + 24.084 + ], + [ + 14.328, + 24.611 + ], + [ + 15.128, + 23.885 + ], + [ + 17.357, + 23.776 + ], + [ + 16.644, + 23.504 + ], + [ + 17.744, + 22.255 + ], + [ + 20.192, + 21.891 + ], + [ + 21.354, + 22.799 + ], + [ + 21.076, + 22.307 + ], + [ + 21.56, + 22.308 + ], + [ + 21.51, + 21.619 + ], + [ + 22.93, + 20.48 + ], + [ + 23.208, + 21.21 + ], + [ + 24.021, + 20.611 + ], + [ + 23.874, + 21.842 + ], + [ + 24.359, + 22.313 + ], + [ + 23.971, + 22.929 + ], + [ + 24.6, + 23.003 + ], + [ + 24.467, + 24.108 + ], + [ + 25.388, + 22.84 + ], + [ + 26.114, + 22.947 + ], + [ + 26.005, + 23.836 + ], + [ + 27.351, + 25.194 + ], + [ + 27.631, + 24.889 + ], + [ + 27.073, + 22.967 + ], + [ + 27.569, + 23.49 + ], + [ + 27.314, + 22.388 + ], + [ + 28.052, + 21.172 + ], + [ + 28.32, + 21.679 + ], + [ + 28.392, + 20.992 + ], + [ + 32.413, + 22.331 + ], + [ + 33.479, + 21.898 + ], + [ + 33.238, + 23.239 + ], + [ + 33.988, + 22.368 + ], + [ + 34.861, + 23.094 + ], + [ + 35.672, + 22.497 + ], + [ + 35.235, + 22.17 + ], + [ + 35.249, + 20.629 + ], + [ + 37.028, + 20.358 + ], + [ + 35.2, + 19.289 + ], + [ + 35.187, + 17.895 + ], + [ + 36.41, + 17.55 + ], + [ + 36.276, + 16.769 + ], + [ + 37.113, + 16.606 + ], + [ + 36.906, + 16.153 + ], + [ + 37.281, + 15.845 + ], + [ + 39.778, + 16.57 + ], + [ + 39.367, + 17.548 + ], + [ + 40.77, + 17.367 + ], + [ + 40.868, + 16.751 + ], + [ + 42.83, + 15.735 + ], + [ + 44.478, + 15.917 + ], + [ + 45.641, + 14.359 + ], + [ + 46.162, + 15.357 + ], + [ + 45.799, + 15.864 + ], + [ + 46.307, + 15.828 + ], + [ + 46.052, + 17.149 + ], + [ + 47.286, + 15.123 + ], + [ + 50.025, + 13.779 + ], + [ + 49.333, + 14.522 + ], + [ + 49.963, + 14.85 + ], + [ + 49.454, + 14.813 + ], + [ + 49.745, + 15.429 + ], + [ + 49.358, + 16.008 + ], + [ + 49.891, + 16.154 + ], + [ + 49.287, + 17.24 + ], + [ + 49.333, + 18.11 + ], + [ + 48.558, + 18.654 + ], + [ + 48.328, + 19.58 + ], + [ + 49.334, + 18.763 + ], + [ + 50.048, + 16.533 + ], + [ + 51.815, + 16.461 + ], + [ + 52.047, + 17.385 + ], + [ + 50.909, + 18.076 + ], + [ + 51.914, + 18.203 + ], + [ + 51.394, + 18.398 + ], + [ + 52.254, + 18.962 + ], + [ + 53.658, + 18.6 + ], + [ + 54.253, + 19.341 + ], + [ + 53.767, + 20.829 + ], + [ + 57.184, + 23.728 + ], + [ + 57.414, + 26.356 + ], + [ + 60.536, + 28.021 + ], + [ + 60.139, + 29.161 + ], + [ + 61.361, + 30.632 + ], + [ + 61.506, + 28.963 + ], + [ + 63.034, + 29.471 + ], + [ + 62.899, + 28.837 + ], + [ + 65.19, + 26.136 + ], + [ + 67.514, + 25.123 + ], + [ + 67.72, + 24.67 + ], + [ + 67.127, + 24.107 + ], + [ + 68.605, + 23.782 + ], + [ + 69.16, + 24.616 + ], + [ + 72.046, + 22.188 + ], + [ + 72.215, + 22.984 + ], + [ + 73.002, + 23.185 + ], + [ + 71.536, + 25.232 + ], + [ + 72.19, + 25.592 + ], + [ + 74.151, + 24.142 + ], + [ + 74.164, + 25.395 + ], + [ + 75.084, + 26.409 + ], + [ + 75.121, + 27.26 + ], + [ + 75.654, + 25.63 + ], + [ + 76.019, + 27.08 + ], + [ + 76.732, + 26.663 + ], + [ + 76.223, + 26.228 + ], + [ + 76.889, + 25.304 + ], + [ + 77.664, + 25.921 + ], + [ + 76.889, + 24.653 + ], + [ + 79.891, + 24.397 + ], + [ + 79.639, + 24.995 + ], + [ + 80.39, + 25.032 + ], + [ + 80.316, + 25.903 + ], + [ + 81.408, + 27.062 + ], + [ + 80.873, + 27.35 + ], + [ + 82.097, + 28.892 + ], + [ + 82.353, + 28.075 + ], + [ + 81.623, + 26.843 + ], + [ + 82.569, + 27.062 + ], + [ + 80.885, + 26.174 + ], + [ + 81.19, + 25.505 + ], + [ + 80.293, + 24.596 + ], + [ + 80.631, + 24.341 + ], + [ + 80.364, + 23.909 + ], + [ + 81.636, + 23.13 + ], + [ + 81.539, + 22.44 + ], + [ + 83.019, + 22.44 + ], + [ + 82.399, + 22.206 + ], + [ + 83.211, + 21.643 + ], + [ + 84.084, + 22.186 + ], + [ + 85.876, + 21.244 + ], + [ + 86.058, + 22.641 + ], + [ + 85.041, + 24.126 + ], + [ + 85.901, + 25.413 + ], + [ + 87.96, + 25.956 + ], + [ + 91.932, + 25.34 + ], + [ + 93.168, + 24.396 + ], + [ + 95.517, + 25.195 + ], + [ + 97.892, + 24.217 + ], + [ + 100.0, + 24.651 + ], + [ + 98.39, + 23.547 + ], + [ + 99.078, + 22.802 + ], + [ + 97.381, + 23.71 + ], + [ + 97.213, + 22.984 + ], + [ + 95.711, + 22.333 + ], + [ + 94.984, + 20.411 + ], + [ + 93.81, + 19.849 + ], + [ + 93.64, + 16.48 + ], + [ + 92.501, + 15.9 + ], + [ + 93.471, + 15.067 + ], + [ + 93.809, + 13.073 + ], + [ + 110.392, + 13.073 + ], + [ + 110.392, + 86.998 + ], + [ + 76.634, + 86.853 + ] ] ], "worldSize": 100, @@ -6960,7 +9288,7 @@ "sourceLengthKm": 1971.7732460448683, "simplificationToleranceDeg": 0.0025, "minCellSizeWorld": 1.5625, - "minCellSizeRealKm": 3.9936135910971187, + "minCellSizeRealKm": 3.9999241458161254, "boundingBox": { "lonMin": -4.797583, "lonMax": -1.3575, @@ -6973,450 +9301,868 @@ "name": "normandie", "points": [ [ - 0.0, - 33.811 + 12.11, + 56.098 ], [ - 0.494, - 34.065 + 12.299, + 56.604 ], [ - 1.317, - 34.172 + 13.507, + 58.364 ], [ - 1.436, - 34.572 + 15.13, + 60.355 ], [ - 1.965, - 34.735 + 15.657, + 61.241 ], [ - 1.505, - 34.9 + 15.821, + 62.078 ], [ - 1.46, - 35.19 + 15.623, + 61.695 ], [ - 2.119, - 34.97 + 15.421, + 62.729 ], [ - 2.106, - 34.589 + 15.517, + 63.344 ], [ - 2.26, - 34.428 + 15.233, + 63.452 ], [ - 3.365, - 34.356 + 15.351, + 63.924 ], [ - 5.509, - 33.775 + 15.575, + 63.617 ], [ - 5.908, - 33.776 + 15.997, + 63.399 ], [ - 5.884, - 33.847 + 16.536, + 63.506 ], [ - 5.578, - 33.847 + 17.021, + 63.851 ], [ - 5.4, - 33.974 + 17.244, + 64.994 ], [ - 5.165, - 34.446 + 17.112, + 63.525 ], [ - 5.25, - 34.754 + 18.344, + 62.166 ], [ - 5.236, - 34.446 + 19.542, + 62.204 ], [ - 5.507, - 33.956 + 19.966, + 61.95 ], [ - 5.907, - 33.883 + 21.445, + 61.877 ], [ - 6.425, - 33.376 + 22.669, + 62.059 ], [ - 8.426, - 32.576 + 23.114, + 62.565 ], [ - 10.543, - 30.943 + 23.96, + 63.074 ], [ - 10.733, - 31.09 + 26.496, + 63.798 ], [ - 10.78, - 30.944 + 28.35, + 64.087 ], [ - 11.111, - 31.523 + 30.277, + 64.015 ], [ - 10.923, - 31.053 + 31.1, + 64.341 ], [ - 10.65, - 30.817 + 33.8, + 64.051 ], [ - 11.227, - 29.964 + 35.702, + 64.487 ], [ - 12.05, - 29.238 + 37.274, + 64.559 ], [ - 13.346, - 28.913 + 38.45, + 65.029 ], [ - 14.545, - 28.296 + 40.094, + 66.154 ], [ - 14.924, - 28.293 + 40.753, + 66.444 ], [ - 15.135, - 28.585 + 41.573, + 66.55 ], [ - 15.499, - 28.675 + 41.692, + 66.95 ], [ - 15.159, - 28.511 + 42.22, + 67.113 ], [ - 15.016, - 28.185 + 41.761, + 67.277 ], [ - 17.065, - 28.077 + 41.717, + 67.567 ], [ - 18.688, - 27.712 + 42.374, + 67.347 ], [ - 19.465, - 28.004 + 42.361, + 66.967 ], [ - 18.984, - 27.659 + 42.514, + 66.806 ], [ - 20.595, - 27.024 + 43.617, + 66.734 ], [ - 22.183, - 25.881 + 45.756, + 66.154 ], [ - 21.869, - 25.861 + 46.155, + 66.155 ], [ - 20.125, - 26.95 + 46.13, + 66.226 ], [ - 18.689, - 27.242 + 45.825, + 66.226 ], [ - 14.97, - 26.988 + 45.647, + 66.353 ], [ - 13.25, - 26.699 + 45.413, + 66.824 ], [ - 12.898, - 26.914 + 45.498, + 67.131 ], [ - 12.758, - 26.622 + 45.484, + 66.824 ], [ - 11.722, - 26.406 + 45.754, + 66.335 ], [ - 11.039, - 25.898 + 46.153, + 66.261 ], [ - 11.015, - 25.717 + 46.67, + 65.755 ], [ - 11.769, - 26.263 + 48.667, + 64.957 ], [ - 12.687, - 26.444 + 50.779, + 63.326 ], [ - 13.206, - 26.297 + 50.969, + 63.473 ], [ - 13.358, - 26.097 + 51.016, + 63.327 ], [ - 12.616, - 26.334 + 51.346, + 63.906 ], [ - 12.064, - 26.135 + 51.158, + 63.436 ], [ - 12.239, - 26.043 + 50.886, + 63.2 ], [ - 11.535, - 25.899 + 51.462, + 62.349 ], [ - 11.485, - 25.789 + 52.283, + 61.624 ], [ - 11.735, - 25.699 + 53.576, + 61.299 ], [ - 11.322, - 25.717 + 54.772, + 60.683 ], [ - 10.923, - 25.064 + 55.15, + 60.681 ], [ - 10.356, - 24.775 + 55.361, + 60.972 ], [ - 10.299, - 24.319 + 55.724, + 61.062 ], [ - 10.72, - 23.013 + 55.385, + 60.898 ], [ - 12.745, - 18.657 + 55.242, + 60.573 ], [ - 13.053, - 16.878 + 57.286, + 60.464 ], [ - 14.263, - 15.699 + 58.906, + 60.101 ], [ - 15.158, - 15.483 + 59.681, + 60.392 ], [ - 16.335, - 14.719 + 59.201, + 60.048 ], [ - 17.865, - 14.321 + 60.809, + 59.414 ], [ - 18.432, - 13.885 + 62.393, + 58.272 ], [ - 18.667, - 13.486 + 62.079, + 58.253 ], [ - 18.784, - 13.631 + 60.34, + 59.34 ], [ - 18.961, - 13.613 + 58.907, + 59.631 ], [ - 18.761, - 13.376 + 55.196, + 59.377 ], [ - 20.856, - 12.361 + 53.48, + 59.089 ], [ - 22.101, - 11.6 + 53.129, + 59.304 ], [ - 22.503, - 11.163 + 52.989, + 59.013 ], [ - 23.139, - 10.873 + 51.956, + 58.796 ], [ - 23.915, - 10.181 + 51.274, + 58.289 ], [ - 24.832, - 9.711 + 51.25, + 58.109 ], [ - 28.318, - 8.768 + 52.002, + 58.654 ], [ - 28.434, - 8.766 + 52.918, + 58.835 ], [ - 28.599, - 9.168 + 53.436, + 58.688 ], [ - 28.528, - 8.804 + 53.587, + 58.488 ], [ - 28.976, - 8.913 + 52.848, + 58.725 ], [ - 30.459, - 8.769 + 52.296, + 58.526 ], [ - 31.872, - 8.078 + 52.471, + 58.434 ], [ - 34.649, - 7.352 + 51.769, + 58.29 + ], + [ + 51.719, + 58.18 + ], + [ + 51.968, + 58.091 + ], + [ + 51.557, + 58.109 + ], + [ + 51.158, + 57.457 + ], + [ + 50.593, + 57.168 + ], + [ + 50.536, + 56.713 + ], + [ + 50.955, + 55.409 + ], + [ + 52.976, + 51.06 + ], + [ + 53.284, + 49.284 + ], + [ + 54.491, + 48.106 + ], + [ + 55.384, + 47.891 + ], + [ + 56.558, + 47.128 + ], + [ + 58.084, + 46.73 + ], + [ + 58.65, + 46.296 + ], + [ + 58.885, + 45.897 + ], + [ + 59.002, + 46.042 + ], + [ + 59.178, + 46.024 + ], + [ + 58.978, + 45.787 + ], + [ + 61.069, + 44.774 + ], + [ + 62.311, + 44.014 + ], + [ + 62.712, + 43.578 + ], + [ + 63.347, + 43.288 + ], + [ + 64.121, + 42.597 + ], + [ + 65.036, + 42.128 + ], + [ + 68.514, + 41.186 ], [ - 35.613, - 6.807 + 68.63, + 41.185 ], [ - 37.403, - 6.917 + 68.795, + 41.586 ], [ - 38.72, - 6.445 + 68.724, + 41.222 ], [ - 39.097, - 6.117 + 69.171, + 41.331 ], [ - 39.155, - 6.354 + 70.65, + 41.187 ], [ - 39.062, - 6.607 + 72.06, + 40.497 ], [ - 39.31, - 6.843 + 74.831, + 39.773 ], [ - 39.182, - 6.5 + 75.793, + 39.228 ], [ - 39.285, - 6.154 + 77.579, + 39.338 ], [ - 42.37, - 4.521 + 78.893, + 38.867 ], [ - 46.041, - 1.236 + 79.269, + 38.539 ], [ - 47.077, - 0.547 + 79.327, + 38.776 ], [ - 47.077, - 0.4 + 79.234, + 39.029 + ], + [ + 79.482, + 39.264 + ], + [ + 79.353, + 38.922 + ], + [ + 79.457, + 38.577 + ], + [ + 82.535, + 36.946 ], [ - 47.314, - 0.437 + 86.197, + 33.667 ], [ - 47.737, - 0.0 + 87.232, + 32.978 + ], + [ + 87.231, + 32.832 + ], + [ + 87.467, + 32.868 + ], + [ + 87.89, + 32.432 + ] + ], + "landRings": [ + [ + [ + 12.145, + 48.975 + ], + [ + 13.015, + 48.794 + ], + [ + 12.839, + 49.538 + ], + [ + 14.037, + 51.968 + ], + [ + 14.002, + 52.745 + ], + [ + 13.239, + 52.654 + ], + [ + 12.732, + 54.304 + ], + [ + 12.757, + 53.652 + ], + [ + 12.053, + 53.866 + ], + [ + 12.145, + 48.975 + ] + ], + [ + [ + 12.11, + 56.098 + ], + [ + 15.657, + 61.241 + ], + [ + 15.351, + 63.924 + ], + [ + 16.536, + 63.506 + ], + [ + 17.244, + 64.994 + ], + [ + 17.112, + 63.525 + ], + [ + 18.344, + 62.166 + ], + [ + 21.445, + 61.877 + ], + [ + 26.496, + 63.798 + ], + [ + 37.274, + 64.559 + ], + [ + 42.22, + 67.113 + ], + [ + 41.761, + 67.277 + ], + [ + 41.717, + 67.567 + ], + [ + 42.374, + 67.347 + ], + [ + 42.514, + 66.806 + ], + [ + 46.155, + 66.155 + ], + [ + 46.13, + 66.226 + ], + [ + 45.825, + 66.226 + ], + [ + 45.647, + 66.353 + ], + [ + 45.413, + 66.824 + ], + [ + 45.498, + 67.131 + ], + [ + 45.754, + 66.335 + ], + [ + 46.153, + 66.261 + ], + [ + 46.67, + 65.755 + ], + [ + 48.667, + 64.957 + ], + [ + 50.779, + 63.326 + ], + [ + 51.346, + 63.906 + ], + [ + 50.886, + 63.2 + ], + [ + 52.283, + 61.624 + ], + [ + 54.772, + 60.683 + ], + [ + 55.15, + 60.681 + ], + [ + 55.361, + 60.972 + ], + [ + 55.724, + 61.062 + ], + [ + 55.242, + 60.573 + ], + [ + 59.681, + 60.392 + ], + [ + 59.201, + 60.048 + ], + [ + 62.393, + 58.272 + ], + [ + 58.907, + 59.631 + ], + [ + 53.129, + 59.304 + ], + [ + 51.274, + 58.289 + ], + [ + 52.918, + 58.835 + ], + [ + 53.587, + 58.488 + ], + [ + 51.769, + 58.29 + ], + [ + 50.536, + 56.713 + ], + [ + 52.976, + 51.06 + ], + [ + 53.284, + 49.284 + ], + [ + 54.491, + 48.106 + ], + [ + 58.084, + 46.73 + ], + [ + 65.036, + 42.128 + ], + [ + 70.65, + 41.187 + ], + [ + 75.793, + 39.228 + ], + [ + 77.579, + 39.338 + ], + [ + 79.269, + 38.539 + ], + [ + 79.234, + 39.029 + ], + [ + 79.482, + 39.264 + ], + [ + 79.457, + 38.577 + ], + [ + 82.535, + 36.946 + ], + [ + 88.148, + 32.234 + ], + [ + 88.148, + 74.832 + ], + [ + 12.053, + 74.832 + ], + [ + 12.11, + 56.098 + ] ] ], "worldSize": 100, "meta": { - "sourceLengthKm": 265.70253627275014, + "sourceLengthKm": 367.17937853828636, "simplificationToleranceDeg": 0.0025, "minCellSizeWorld": 1.5625, - "minCellSizeRealKm": 3.9936135910971187, + "minCellSizeRealKm": 3.9999241458161254, "boundingBox": { - "lonMin": -0.299167, + "lonMin": -1.297944, "lonMax": 1.390833, "latMin": 49.267056, "latMax": 50.075028 diff --git a/website/MDX_MIGRATION.md b/website/MDX_MIGRATION.md new file mode 100644 index 000000000..c46480e97 --- /dev/null +++ b/website/MDX_MIGRATION.md @@ -0,0 +1,298 @@ +# Blog-Stack: Migration nach MDX und Vike-nativem Rendering + +Arbeitsplan in drei Stufen. Jede Stufe ist einzeln lieferbar und einzeln wertvoll — +nach jeder kann abgebrochen werden, ohne dass ein halber Umbau liegen bleibt. + +--- + +## Ausgangslage + +Verifiziert am Stand `coast_post` (2026-08-23): + +| Fakt | Beleg | +|---|---| +| 29 MDX-Posts, 5 TSX-Posts im Blog | `ls blog/` | +| 33 MDX-Vorlesungen in 4 `quantum/`-Ordnern | `quantum/{amo,basics,hardware,qml}` | +| **34 von 36** prerenderten Seiten enthalten als Artikeltext nur `🔄 Lade interaktive Komponente...` | `grep -rl` über `build/blog/*/index.html` | +| Betroffen sind auch reine Prosa-Posts ohne jede Interaktivität | `blog/ipfs.mdx` (4.773 B Markdown, 0 Imports) → 477 Zeichen sichtbarer Text im HTML, davon der Großteil Navigation | +| `prerender: true` ist aktiv — Prerendering läuft, der Blog-Inhalt entwischt ihm | `pages/+config.ts:24` | +| Ursache: `ReactPostRenderer` lädt den Post in einem `useEffect`, der in Node nie feuert | `components/Post.tsx:41-85` | +| `vike-react` bietet `clientOnly()` und `` — im Repo **nirgends** benutzt | `node_modules/vike-react/dist/helpers/clientOnly.d.ts` | +| Vike akzeptiert `.mdx` als vollwertige `+Page`-Endung | `node_modules/vike/dist/utils/isScriptFile.js` → `extTemplates` enthält `'mdx'` | +| `stream: { require: true, disable: true }` — SSR per Stream, Ergebnis wird zu einem String zusammengefasst | `node_modules/vike-react/dist/types/Config.d.ts:158-182` | + +### Latenter Bug, unabhängig von allem anderen + +Die URL eines Posts ist sein **Index im nach `publishing_date` sortierten Array** +(`utils/blogLoader.ts:137-144`, `pages/blog/@id/+onBeforePrerenderStart.js`). Der Kommentar +im Code („oldest first to keep stable URLs") schützt nur davor, dass *neuere* Posts die +alten verschieben. Ein Post mit **rückdatiertem** `publishing_date` verschiebt jede URL +danach um eins — und bricht damit Links, Webmentions und die NFT-Zuordnung. + +Stufe 2.5 unten behebt das. Es lohnt sich auch dann, wenn Stufe 3 nie kommt. + +--- + +## Stufe 1 — TSX-Posts nach MDX konvertieren + +**Ziel:** Prosa wird Markdown, Widgets werden benannte Komponenten in `components/blog/`. +**Wert für sich allein:** Wartbarkeit, ein Dateiformat, ~60 Zeilen Sonderbehandlung weniger. +**Der eigentliche Wert:** ohne diese Trennung ist Stufe 2 unmöglich (siehe unten). + +### Warum das die Bedingung für Stufe 2 ist + +In `blog/prisoners_dilemma_interactive.tsx:17` läuft `ChartJS.register(...)` auf Modulebene. +Das Modul zu importieren zieht Chart.js in den Node-Build — und Chart.js braucht ein DOM. +Solange Prosa und Chart im selben Modul liegen, gibt es keine Granularität: entweder der +ganze Post rendert in Node oder gar keiner. Aktuell: gar keiner, für **alle** Posts. + +MDX zerlegt das. Vorbild ist `blog/etf_diversification_interactive.mdx` — Prosa als +Markdown, Widgets als Imports aus `components/blog/`. Das Muster existiert bereits und +funktioniert. + +### Reihenfolge + +Aufsteigend nach Schwierigkeit, nicht nach Größe. + +- [ ] **1.1 `budget_gridlock_interactive.tsx`** (669 Z., 262 Z. Prosa in Template-Strings, chart.js) + Pilot. Höchster Prosa-Anteil, der bereits als Markdown vorliegt. +- [ ] **1.2 `tragedy_of_commons_fishing.tsx`** (2025 Z., 140 Z. Markdown-Strings) +- [ ] **1.3 `prisoners_dilemma_interactive.tsx`** (1606 Z., 190 Z. Strings + 18 `

` in JSX, chart.js) +- [ ] **1.4 `merkle_ai_batching.tsx`** (381 Z., reine JSX-Prosa, 23 `

`, Mermaid) +- [ ] **1.5 `merkle_ai_batching_fundamentals.tsx`** (1243 Z., reine JSX-Prosa, 21 `

`, Mermaid) + +1.1–1.3 sind weitgehend mechanisch: die Prosa liegt dort schon als Markdown vor und wird +heute nur **zur Laufzeit im Browser** von `react-markdown` geparst statt zur Buildzeit von +MDX. Das ist eine echte Dopplung der Pipeline. + +1.4/1.5 sind Handarbeit: Prosa ist mit Panda-`css()` in JSX verwoben. + +### Pro Datei + +1. Widgets nach `components/blog/.tsx` extrahieren, Default-Export. +2. Prosa nach Markdown; `{\`...\`}` → Fließtext. +3. `export const meta = {…}` → Frontmatter. Mapping ist 1:1: + `title`, `publishing_date`, `tokenID`, `description`, `category`, `secondaryCategory`, `order`. +4. `publishing_date` **unverändert** übernehmen — davon hängt die URL ab. + +### Fallen + +- **`publishing_date` ändern verschiebt URLs.** Siehe latenter Bug oben. +- **Code-Blöcke laufen ab jetzt durch `MdxPre`** (`components/Post.tsx:143`). TSX-Posts + haben den `components`-Prop ignoriert, MDX nicht. Optische Änderung möglich, prüfen. +- **LaTeX:** MDX schützt `$$…$$` über `remark-math` (`vite.config.ts`). Der klassische + Bruch sind Unterstriche in Formeln, die sonst zu `` werden. Jede Formel nach der + Konvertierung im Browser ansehen. +- **Panda:** die inline-`css()`-Boxen aus 1.4/1.5 müssen entweder eigene Komponenten + werden oder in Markdown aufgehen. Kein `css()` mit JS-Variablen — siehe `CLAUDE.md` Regel 1. +- **Mermaid** (`components/MermaidDiagram.tsx`) wird in Stufe 2 ein `clientOnly()`-Kandidat. + +### Aufräumen, wenn alle fünf durch sind + +- [ ] `utils/blogLoader.ts:27,51-62,66-71` — TSX-Zweig und zweite Fallback-Titel-Regel entfernen +- [ ] `utils/globRegistry.ts` + `utils/lazyGlobRegistry.ts` — 10× `*.{tsx,mdx}` → `*.mdx` +- [ ] `types/BlogPost.ts:12-21` — `BlogPostMeta` löschen +- [ ] `types/BlogPost.ts` — `PostType` ist tot: `blogLoader.ts:77` setzt immer `"react"`, niemand liest es +- [ ] `components/MarkdownWithLatex.tsx` löschen (danach ungenutzt; `react-markdown` bleibt als Dependency für `AssistantChat.tsx`) +- [ ] Tests: `test/blogLoader.unit.test.ts:117,141`, `test/blogLoader.test.ts:128-133`, `test/blogLoader.integration.test.ts:29,50`, `test/blogLoader.nft.test.ts:29` + +### Abnahme + +``` +npm run build +``` + +Dann `build/blog/*/index.html` vorher/nachher vergleichen: **die Menge der URLs und die +Titel-zu-URL-Zuordnung muss identisch sein.** Alles andere ist ein Bug, kein Fortschritt. +Zusätzlich `npm test`, `npm run typecheck`, `npm run lint`. + +--- + +## Stufe 2 — Inhalt ins HTML bringen + +**Ziel:** Der Artikeltext steht in der Datei, nicht im JavaScript. +**Wert:** Bridgy Fed / Webmentions bekommen echten Inhalt statt `🔄 Lade interaktive +Komponente...` (siehe die Klage in `components/Post.tsx:136-140`), SEO, LCP, ToC ohne JS. +**Betrifft alle 36 Seiten**, nicht nur die fünf aus Stufe 1. + +### 2.0 Spike zuerst — das ist die entscheidende offene Frage + +Der naheliegende Weg ist `React.lazy` + `` statt `useEffect`, zusammen mit +`stream: { require: true, disable: true }` in `pages/+config.ts`. Streaming-SSR löst +`lazy` auf, und `disable: true` fasst den Stream zu einem vollständigen HTML-String +zusammen — genau das, was eine statische Datei braucht. + +**Das Risiko:** React liefert spät aufgelösten Suspense-Inhalt als `