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.
- cinematicearth-0.2.0/LICENSE +21 -0
- cinematicearth-0.2.0/PKG-INFO +223 -0
- cinematicearth-0.2.0/README.md +169 -0
- cinematicearth-0.2.0/pyproject.toml +49 -0
- cinematicearth-0.2.0/setup.cfg +4 -0
- cinematicearth-0.2.0/src/CinematicEarth.egg-info/PKG-INFO +223 -0
- cinematicearth-0.2.0/src/CinematicEarth.egg-info/SOURCES.txt +23 -0
- cinematicearth-0.2.0/src/CinematicEarth.egg-info/dependency_links.txt +1 -0
- cinematicearth-0.2.0/src/CinematicEarth.egg-info/entry_points.txt +2 -0
- cinematicearth-0.2.0/src/CinematicEarth.egg-info/requires.txt +30 -0
- cinematicearth-0.2.0/src/CinematicEarth.egg-info/top_level.txt +1 -0
- cinematicearth-0.2.0/src/cinematicearth/__init__.py +45 -0
- cinematicearth-0.2.0/src/cinematicearth/cli.py +176 -0
- cinematicearth-0.2.0/src/cinematicearth/esp.py +206 -0
- cinematicearth-0.2.0/src/cinematicearth/geo.py +130 -0
- cinematicearth-0.2.0/src/cinematicearth/geocode.py +82 -0
- cinematicearth-0.2.0/src/cinematicearth/io.py +208 -0
- cinematicearth-0.2.0/src/cinematicearth/keyframes.py +105 -0
- cinematicearth-0.2.0/src/cinematicearth/kml.py +31 -0
- cinematicearth-0.2.0/src/cinematicearth/model.py +217 -0
- cinematicearth-0.2.0/src/cinematicearth/overlay.py +317 -0
- cinematicearth-0.2.0/src/cinematicearth/planner.py +552 -0
- cinematicearth-0.2.0/src/cinematicearth/preview.py +55 -0
- cinematicearth-0.2.0/src/cinematicearth/simulate.py +156 -0
- cinematicearth-0.2.0/tests/test_core.py +156 -0
|
@@ -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
|
+
[](https://pypi.org/project/CinematicEarth/) [](https://github.com/MayerT1/CinematicEarth/actions/workflows/tests.yml) [](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
|
+
[](https://pypi.org/project/CinematicEarth/) [](https://github.com/MayerT1/CinematicEarth/actions/workflows/tests.yml) [](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"]
|