CinematicEarth 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tim Mayer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,223 @@
1
+ Metadata-Version: 2.4
2
+ Name: CinematicEarth
3
+ Version: 0.2.0
4
+ Summary: Cinematic multi-stop flyovers for Google Earth Studio: stop list + camera moves -> .esp project, KML tour, annotated MP4
5
+ Author-email: Tim Mayer <tjm0042@uah.edu>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/MayerT1/CinematicEarth
8
+ Project-URL: Repository, https://github.com/MayerT1/CinematicEarth
9
+ Project-URL: Issues, https://github.com/MayerT1/CinematicEarth/issues
10
+ Project-URL: Colab, https://colab.research.google.com/github/MayerT1/CinematicEarth/blob/main/examples/CinematicEarth_colab.ipynb
11
+ Keywords: google earth studio,esp,flyover,kml,geospatial,animation
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: Intended Audience :: Education
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Scientific/Engineering :: GIS
24
+ Classifier: Topic :: Multimedia :: Video
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: numpy>=1.21
29
+ Provides-Extra: yaml
30
+ Requires-Dist: pyyaml; extra == "yaml"
31
+ Provides-Extra: plot
32
+ Requires-Dist: matplotlib; extra == "plot"
33
+ Provides-Extra: video
34
+ Requires-Dist: opencv-python-headless; extra == "video"
35
+ Requires-Dist: pillow; extra == "video"
36
+ Requires-Dist: tqdm; extra == "video"
37
+ Requires-Dist: imageio-ffmpeg; extra == "video"
38
+ Provides-Extra: all
39
+ Requires-Dist: pyyaml; extra == "all"
40
+ Requires-Dist: matplotlib; extra == "all"
41
+ Requires-Dist: opencv-python-headless; extra == "all"
42
+ Requires-Dist: pillow; extra == "all"
43
+ Requires-Dist: tqdm; extra == "all"
44
+ Requires-Dist: imageio-ffmpeg; extra == "all"
45
+ Provides-Extra: dev
46
+ Requires-Dist: pytest; extra == "dev"
47
+ Requires-Dist: pyyaml; extra == "dev"
48
+ Requires-Dist: matplotlib; extra == "dev"
49
+ Requires-Dist: opencv-python-headless; extra == "dev"
50
+ Requires-Dist: pillow; extra == "dev"
51
+ Requires-Dist: tqdm; extra == "dev"
52
+ Requires-Dist: imageio-ffmpeg; extra == "dev"
53
+ Dynamic: license-file
54
+
55
+ # CinematicEarth
56
+
57
+ [![PyPI](https://img.shields.io/pypi/v/CinematicEarth.svg)](https://pypi.org/project/CinematicEarth/) [![tests](https://github.com/MayerT1/CinematicEarth/actions/workflows/tests.yml/badge.svg)](https://github.com/MayerT1/CinematicEarth/actions/workflows/tests.yml) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/MayerT1/CinematicEarth/blob/main/examples/CinematicEarth_colab.ipynb)
58
+
59
+ Cinematic multi-stop flyovers for **Google Earth Studio**. You give it a list of stops (and, if you want, camera moves per stop). It writes:
60
+
61
+ - **`.esp`**: an Earth Studio project. Open it, check it, render.
62
+ - **`.kml`**: the same camera path as a Google Earth Pro tour (fallback renderer).
63
+ - **`.plan.json`**: timing for each stop, used to burn animated **annotations** into the render.
64
+
65
+ What the camera does:
66
+
67
+ - **At each city:** a slow, tilted orbit (default **45°** over 8 s) that pulls in from 7 km to 3.5 km. Pan and tilt are computed per keyframe, so **the city is locked to screen centre** (< 1 px drift in tests).
68
+ - **Between nearby cities:** a low, forward-looking glide.
69
+ - **Between distant cities:** a smooth arc up toward space and a dive back into the tilted view.
70
+ - **Pacing:** every hop's duration comes from a cap on *perceived screen speed*, so nothing rockets across the map.
71
+
72
+ ```text
73
+ stops.md ──► cinematicearth build ──► flyover.esp ──► Earth Studio render (.zip / .mp4)
74
+ ├► flyover.kml │
75
+ └► flyover.plan.json ──► cinematicearth annotate ──► flyover_final.mp4
76
+ ```
77
+
78
+ ## Install
79
+
80
+ ```bash
81
+ pip install "CinematicEarth[all]" # everything: plots, YAML, annotated video (bundled ffmpeg)
82
+ pip install CinematicEarth # core only (numpy): build .esp / .kml
83
+ pip install "git+https://github.com/MayerT1/CinematicEarth.git" # latest from GitHub
84
+ # local dev: pip install -e ".[dev]"
85
+ ```
86
+
87
+ The `annotate` and `simulate` commands use `ffmpeg` from your PATH, or the copy bundled with `imageio-ffmpeg`, which the `[video]`, `[all]` and `[dev]` extras install. That means it works on Windows without installing ffmpeg separately.
88
+
89
+ ## Quick start
90
+
91
+ ```bash
92
+ cinematicearth init stops.md # example file
93
+ cinematicearth build stops.md -o flyover.esp # + flyover.kml, flyover.plan.json
94
+ cinematicearth build examples/stops_18.md --speed medium --stop-duration 6
95
+ cinematicearth build "Huntsville, AL; Santa Fe, NM; Kathmandu" --geocode --elevation
96
+ cinematicearth preview stops.md -o preview.png # ground track / altitude / pacing plots
97
+ cinematicearth simulate flyover.esp flyover.plan.json -o wire.mp4 # wireframe motion check
98
+ cinematicearth annotate flyover.plan.json render.zip -o flyover_final.mp4
99
+ cinematicearth smoketest # tiny 10 s .esp to check Earth Studio import
100
+ cinematicearth moves # camera-move reference
101
+ ```
102
+
103
+ ```python
104
+ import cinematicearth as ce
105
+ plan = ce.build("examples/stops_18.md", out="flyover.esp", speed="slow", stop_duration=8)
106
+ print(plan.summary())
107
+
108
+ # or straight from Python
109
+ plan = ce.build([
110
+ {"name": "Huntsville, AL", "lat": 34.7304, "lon": -86.5861, "elev": 190, "moves": "orbit 60 + zoom 8km>3km"},
111
+ ("Santa Fe, NM", 35.6870, -105.9378, 2130),
112
+ ], out="two.esp", duration=60)
113
+ ```
114
+
115
+ ## Stop files
116
+
117
+ Markdown with optional front matter for settings. Each stop is one list line, `name | lat, lon[, elev] | moves | key=value`:
118
+
119
+ ```markdown
120
+ ---
121
+ title: EarthRISE flyover
122
+ speed: slow # glacial | slow | medium | fast | number
123
+ stop_duration: 8
124
+ orbit: 45
125
+ tilt: 60
126
+ # duration: 240 # force a total length; transits get re-paced to fit
127
+ ---
128
+ - Huntsville, AL | 34.7304, -86.5861, 190 | orbit 60 + zoom 8km>3km; hold 1s | note=NASA EarthRISE / UAH
129
+ - Santa Fe, NM | 35.6870, -105.9378, 2130
130
+ - Kathmandu, Nepal # no coords -> needs --geocode
131
+ ```
132
+
133
+ Other accepted inputs:
134
+
135
+ - A Markdown pipe table (`| name | lat | lon | elev | duration | moves | transit | note |`)
136
+ - `.csv` with the same columns
137
+ - `.json` / `.yaml` (`{"settings": {...}, "stops": [...]}`)
138
+ - `.txt` with one stop per line
139
+ - An inline string `"A; B; C"`
140
+ - A Python list
141
+
142
+ Per-stop keys:
143
+
144
+ | key | meaning |
145
+ |---|---|
146
+ | `elev` | ground elevation (m) of the city. Keeps the camera height right in Santa Fe, Thimphu and other high places. Use `--elevation` to fetch it. |
147
+ | `duration` | seconds at this stop (overrides `stop_duration`) |
148
+ | `transit` | how to **arrive** here: `auto` / `glide` / `hop` / `cut` |
149
+ | `heading` | start pan direction (default: the direction of travel) |
150
+ | `label` | on-screen title |
151
+ | `note` | extra annotation line |
152
+
153
+ ## Camera moves
154
+
155
+ Segments are separated by `;` and run one after another. Moves joined by `+` run at the same time. A segment can take a duration (`@6s`). Untimed segments share whatever is left of the stop's duration.
156
+
157
+ | move | effect |
158
+ |---|---|
159
+ | `orbit 45` (`pan`, `circle`, `spin`) | circle the city 45°. Negative = the other direction |
160
+ | `zoom 8km>3km`, `zoom 2.5km`, `zoom in/out` | change the LookAt range (pull in / push out) |
161
+ | `tilt 55>70` | change the tilt (0 = straight down, 90 = horizon) |
162
+ | `truck 2km` (`flyby`, `slide`) | slide the camera sideways past the city, keeping it centred (parallax flyby) |
163
+ | `rise 1km` (`crane`) | raise or lower the camera, keeping the city centred |
164
+ | `hold` | no motion |
165
+ | `slow 1.5` / `fast 2` | stretch or shrink this stop's time |
166
+
167
+ Example: `orbit 30 + zoom 9km>5km @5s; orbit 30 + tilt 60>72 + zoom 2.5km; hold 1s`
168
+
169
+ ## Pacing (the "not too fast" part)
170
+
171
+ Each transit is reparameterised by perceived on-screen motion: sideways ground motion relative to range, zoom rate, pan rate and tilt rate. Its duration is then set so the **peak** never goes above `speed`.
172
+
173
+ | preset | 18-stop example |
174
+ |---|---|
175
+ | `glacial` | ~8.5 min |
176
+ | `slow` | ~6.5 min (default) |
177
+ | `medium` | ~5 min |
178
+ | `fast` | ~4.3 min |
179
+
180
+ If you set `duration`, all transits share the remaining time at the same speed, so no single hop is singled out to be rushed. You get a warning if that forces them past the preset.
181
+
182
+ For a short cut of many far-apart stops, use `cut_km: 5000`: long hops become hard cuts, and `annotate` adds a dip to black. The 18-stop example with `speed: medium`, `stop_duration: 6` and `cut_km: 5000` runs about 4 min.
183
+
184
+ ## Earth Studio workflow
185
+
186
+ 1. `cinematicearth build stops.md --split 90`. This writes `flyover_part01.esp`, `flyover_part02.esp` and so on, each about 90 s with about 130 keyframes.
187
+ 2. Earth Studio: **File → Import** each part, scrub the timeline to check it, then **Render**. Advanced render gives JPEG frames as a `.zip`.
188
+ 3. `cinematicearth annotate flyover.plan.json part01.zip part02.zip ...` joins the parts into one annotated MP4.
189
+
190
+ Parts are split at the end of a stop, where the camera is momentarily still, so they join seamlessly.
191
+
192
+ **Keep projects light.** A browser tab running Earth Studio can hang or crash on very large projects. Version 0.1.0 wrote about 2,400 keyframes per track over 6.5 min, and Chrome crashed on import.
193
+
194
+ The planner now samples the camera densely, then keeps only the keyframes Earth Studio needs for its own linear interpolation to reproduce the path:
195
+ - **At stops:** the city stays within 3 px of screen centre (`--keyframe-tol`).
196
+ - **In transit:** looser limits, since nothing is pinned to the centre there.
197
+
198
+ If an import still hangs, run `cinematicearth smoketest` and import `test_tiny.esp`. It's 10 s with 15 keyframes. If that works, use a smaller `--split`. If it doesn't, use the template route below.
199
+
200
+ You need to credit **Google Earth** on screen when you publish Earth Studio footage. `annotate` adds the credit by default. Turn it off with `--attribution ""`.
201
+
202
+ If Earth Studio ever refuses the generated project, use one it made itself:
203
+
204
+ 1. Export any small project from your Earth Studio as `template.esp`.
205
+ 2. Run `cinematicearth build stops.md --template template.esp`.
206
+
207
+ The camera tracks are written into your file's own structure, keeping everything else in it.
208
+
209
+ ## How the .esp is written
210
+
211
+ Earth Studio's project format isn't documented. The encodings here match a real Earth Studio export (`modelVersion` 17) as reverse-engineered by [mkatzef/google-studio-utils](https://github.com/mkatzef/google-studio-utils):
212
+
213
+ - Keyframe time is a fraction of the project duration.
214
+ - Longitude and latitude are normalised from a `minValueRange`.
215
+ - Altitude is `m × 1.5357e-8`.
216
+ - `rotationX` (pan / compass heading) is normalised to `minValueRange`..`maxValueRange`.
217
+ - `rotationY` (tilt) is `deg / 180`.
218
+
219
+ Pan and tilt are written for every keyframe. Keyframes are error-bounded and reduced (see `cinematicearth/keyframes.py`). Pan is re-centred per project to stay within about ±200°, and orbit direction alternates between stops by default (`direction: alternate`). Camera Target is left off, so nothing depends on how target influence behaves.
220
+
221
+ ## License
222
+
223
+ MIT
@@ -0,0 +1,169 @@
1
+ # CinematicEarth
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/CinematicEarth.svg)](https://pypi.org/project/CinematicEarth/) [![tests](https://github.com/MayerT1/CinematicEarth/actions/workflows/tests.yml/badge.svg)](https://github.com/MayerT1/CinematicEarth/actions/workflows/tests.yml) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/MayerT1/CinematicEarth/blob/main/examples/CinematicEarth_colab.ipynb)
4
+
5
+ Cinematic multi-stop flyovers for **Google Earth Studio**. You give it a list of stops (and, if you want, camera moves per stop). It writes:
6
+
7
+ - **`.esp`**: an Earth Studio project. Open it, check it, render.
8
+ - **`.kml`**: the same camera path as a Google Earth Pro tour (fallback renderer).
9
+ - **`.plan.json`**: timing for each stop, used to burn animated **annotations** into the render.
10
+
11
+ What the camera does:
12
+
13
+ - **At each city:** a slow, tilted orbit (default **45°** over 8 s) that pulls in from 7 km to 3.5 km. Pan and tilt are computed per keyframe, so **the city is locked to screen centre** (< 1 px drift in tests).
14
+ - **Between nearby cities:** a low, forward-looking glide.
15
+ - **Between distant cities:** a smooth arc up toward space and a dive back into the tilted view.
16
+ - **Pacing:** every hop's duration comes from a cap on *perceived screen speed*, so nothing rockets across the map.
17
+
18
+ ```text
19
+ stops.md ──► cinematicearth build ──► flyover.esp ──► Earth Studio render (.zip / .mp4)
20
+ ├► flyover.kml │
21
+ └► flyover.plan.json ──► cinematicearth annotate ──► flyover_final.mp4
22
+ ```
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install "CinematicEarth[all]" # everything: plots, YAML, annotated video (bundled ffmpeg)
28
+ pip install CinematicEarth # core only (numpy): build .esp / .kml
29
+ pip install "git+https://github.com/MayerT1/CinematicEarth.git" # latest from GitHub
30
+ # local dev: pip install -e ".[dev]"
31
+ ```
32
+
33
+ The `annotate` and `simulate` commands use `ffmpeg` from your PATH, or the copy bundled with `imageio-ffmpeg`, which the `[video]`, `[all]` and `[dev]` extras install. That means it works on Windows without installing ffmpeg separately.
34
+
35
+ ## Quick start
36
+
37
+ ```bash
38
+ cinematicearth init stops.md # example file
39
+ cinematicearth build stops.md -o flyover.esp # + flyover.kml, flyover.plan.json
40
+ cinematicearth build examples/stops_18.md --speed medium --stop-duration 6
41
+ cinematicearth build "Huntsville, AL; Santa Fe, NM; Kathmandu" --geocode --elevation
42
+ cinematicearth preview stops.md -o preview.png # ground track / altitude / pacing plots
43
+ cinematicearth simulate flyover.esp flyover.plan.json -o wire.mp4 # wireframe motion check
44
+ cinematicearth annotate flyover.plan.json render.zip -o flyover_final.mp4
45
+ cinematicearth smoketest # tiny 10 s .esp to check Earth Studio import
46
+ cinematicearth moves # camera-move reference
47
+ ```
48
+
49
+ ```python
50
+ import cinematicearth as ce
51
+ plan = ce.build("examples/stops_18.md", out="flyover.esp", speed="slow", stop_duration=8)
52
+ print(plan.summary())
53
+
54
+ # or straight from Python
55
+ plan = ce.build([
56
+ {"name": "Huntsville, AL", "lat": 34.7304, "lon": -86.5861, "elev": 190, "moves": "orbit 60 + zoom 8km>3km"},
57
+ ("Santa Fe, NM", 35.6870, -105.9378, 2130),
58
+ ], out="two.esp", duration=60)
59
+ ```
60
+
61
+ ## Stop files
62
+
63
+ Markdown with optional front matter for settings. Each stop is one list line, `name | lat, lon[, elev] | moves | key=value`:
64
+
65
+ ```markdown
66
+ ---
67
+ title: EarthRISE flyover
68
+ speed: slow # glacial | slow | medium | fast | number
69
+ stop_duration: 8
70
+ orbit: 45
71
+ tilt: 60
72
+ # duration: 240 # force a total length; transits get re-paced to fit
73
+ ---
74
+ - Huntsville, AL | 34.7304, -86.5861, 190 | orbit 60 + zoom 8km>3km; hold 1s | note=NASA EarthRISE / UAH
75
+ - Santa Fe, NM | 35.6870, -105.9378, 2130
76
+ - Kathmandu, Nepal # no coords -> needs --geocode
77
+ ```
78
+
79
+ Other accepted inputs:
80
+
81
+ - A Markdown pipe table (`| name | lat | lon | elev | duration | moves | transit | note |`)
82
+ - `.csv` with the same columns
83
+ - `.json` / `.yaml` (`{"settings": {...}, "stops": [...]}`)
84
+ - `.txt` with one stop per line
85
+ - An inline string `"A; B; C"`
86
+ - A Python list
87
+
88
+ Per-stop keys:
89
+
90
+ | key | meaning |
91
+ |---|---|
92
+ | `elev` | ground elevation (m) of the city. Keeps the camera height right in Santa Fe, Thimphu and other high places. Use `--elevation` to fetch it. |
93
+ | `duration` | seconds at this stop (overrides `stop_duration`) |
94
+ | `transit` | how to **arrive** here: `auto` / `glide` / `hop` / `cut` |
95
+ | `heading` | start pan direction (default: the direction of travel) |
96
+ | `label` | on-screen title |
97
+ | `note` | extra annotation line |
98
+
99
+ ## Camera moves
100
+
101
+ Segments are separated by `;` and run one after another. Moves joined by `+` run at the same time. A segment can take a duration (`@6s`). Untimed segments share whatever is left of the stop's duration.
102
+
103
+ | move | effect |
104
+ |---|---|
105
+ | `orbit 45` (`pan`, `circle`, `spin`) | circle the city 45°. Negative = the other direction |
106
+ | `zoom 8km>3km`, `zoom 2.5km`, `zoom in/out` | change the LookAt range (pull in / push out) |
107
+ | `tilt 55>70` | change the tilt (0 = straight down, 90 = horizon) |
108
+ | `truck 2km` (`flyby`, `slide`) | slide the camera sideways past the city, keeping it centred (parallax flyby) |
109
+ | `rise 1km` (`crane`) | raise or lower the camera, keeping the city centred |
110
+ | `hold` | no motion |
111
+ | `slow 1.5` / `fast 2` | stretch or shrink this stop's time |
112
+
113
+ Example: `orbit 30 + zoom 9km>5km @5s; orbit 30 + tilt 60>72 + zoom 2.5km; hold 1s`
114
+
115
+ ## Pacing (the "not too fast" part)
116
+
117
+ Each transit is reparameterised by perceived on-screen motion: sideways ground motion relative to range, zoom rate, pan rate and tilt rate. Its duration is then set so the **peak** never goes above `speed`.
118
+
119
+ | preset | 18-stop example |
120
+ |---|---|
121
+ | `glacial` | ~8.5 min |
122
+ | `slow` | ~6.5 min (default) |
123
+ | `medium` | ~5 min |
124
+ | `fast` | ~4.3 min |
125
+
126
+ If you set `duration`, all transits share the remaining time at the same speed, so no single hop is singled out to be rushed. You get a warning if that forces them past the preset.
127
+
128
+ For a short cut of many far-apart stops, use `cut_km: 5000`: long hops become hard cuts, and `annotate` adds a dip to black. The 18-stop example with `speed: medium`, `stop_duration: 6` and `cut_km: 5000` runs about 4 min.
129
+
130
+ ## Earth Studio workflow
131
+
132
+ 1. `cinematicearth build stops.md --split 90`. This writes `flyover_part01.esp`, `flyover_part02.esp` and so on, each about 90 s with about 130 keyframes.
133
+ 2. Earth Studio: **File → Import** each part, scrub the timeline to check it, then **Render**. Advanced render gives JPEG frames as a `.zip`.
134
+ 3. `cinematicearth annotate flyover.plan.json part01.zip part02.zip ...` joins the parts into one annotated MP4.
135
+
136
+ Parts are split at the end of a stop, where the camera is momentarily still, so they join seamlessly.
137
+
138
+ **Keep projects light.** A browser tab running Earth Studio can hang or crash on very large projects. Version 0.1.0 wrote about 2,400 keyframes per track over 6.5 min, and Chrome crashed on import.
139
+
140
+ The planner now samples the camera densely, then keeps only the keyframes Earth Studio needs for its own linear interpolation to reproduce the path:
141
+ - **At stops:** the city stays within 3 px of screen centre (`--keyframe-tol`).
142
+ - **In transit:** looser limits, since nothing is pinned to the centre there.
143
+
144
+ If an import still hangs, run `cinematicearth smoketest` and import `test_tiny.esp`. It's 10 s with 15 keyframes. If that works, use a smaller `--split`. If it doesn't, use the template route below.
145
+
146
+ You need to credit **Google Earth** on screen when you publish Earth Studio footage. `annotate` adds the credit by default. Turn it off with `--attribution ""`.
147
+
148
+ If Earth Studio ever refuses the generated project, use one it made itself:
149
+
150
+ 1. Export any small project from your Earth Studio as `template.esp`.
151
+ 2. Run `cinematicearth build stops.md --template template.esp`.
152
+
153
+ The camera tracks are written into your file's own structure, keeping everything else in it.
154
+
155
+ ## How the .esp is written
156
+
157
+ Earth Studio's project format isn't documented. The encodings here match a real Earth Studio export (`modelVersion` 17) as reverse-engineered by [mkatzef/google-studio-utils](https://github.com/mkatzef/google-studio-utils):
158
+
159
+ - Keyframe time is a fraction of the project duration.
160
+ - Longitude and latitude are normalised from a `minValueRange`.
161
+ - Altitude is `m × 1.5357e-8`.
162
+ - `rotationX` (pan / compass heading) is normalised to `minValueRange`..`maxValueRange`.
163
+ - `rotationY` (tilt) is `deg / 180`.
164
+
165
+ Pan and tilt are written for every keyframe. Keyframes are error-bounded and reduced (see `cinematicearth/keyframes.py`). Pan is re-centred per project to stay within about ±200°, and orbit direction alternates between stops by default (`direction: alternate`). Camera Target is left off, so nothing depends on how target influence behaves.
166
+
167
+ ## License
168
+
169
+ MIT
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "CinematicEarth"
7
+ version = "0.2.0"
8
+ description = "Cinematic multi-stop flyovers for Google Earth Studio: stop list + camera moves -> .esp project, KML tour, annotated MP4"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Tim Mayer", email = "tjm0042@uah.edu" }]
14
+ keywords = ["google earth studio", "esp", "flyover", "kml", "geospatial", "animation"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Science/Research",
18
+ "Intended Audience :: Education",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Topic :: Scientific/Engineering :: GIS",
28
+ "Topic :: Multimedia :: Video",
29
+ ]
30
+ dependencies = ["numpy>=1.21"]
31
+
32
+ [project.optional-dependencies]
33
+ yaml = ["pyyaml"]
34
+ plot = ["matplotlib"]
35
+ video = ["opencv-python-headless", "pillow", "tqdm", "imageio-ffmpeg"]
36
+ all = ["pyyaml", "matplotlib", "opencv-python-headless", "pillow", "tqdm", "imageio-ffmpeg"]
37
+ dev = ["pytest", "pyyaml", "matplotlib", "opencv-python-headless", "pillow", "tqdm", "imageio-ffmpeg"]
38
+
39
+ [project.scripts]
40
+ cinematicearth = "cinematicearth.cli:main"
41
+
42
+ [project.urls]
43
+ Homepage = "https://github.com/MayerT1/CinematicEarth"
44
+ Repository = "https://github.com/MayerT1/CinematicEarth"
45
+ Issues = "https://github.com/MayerT1/CinematicEarth/issues"
46
+ Colab = "https://colab.research.google.com/github/MayerT1/CinematicEarth/blob/main/examples/CinematicEarth_colab.ipynb"
47
+
48
+ [tool.setuptools.packages.find]
49
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+