← All Scripts
3D Visualization Script

Earthquake City Flyover

Build a 3D city from live seismic data. Each building is an earthquake: height is magnitude, color is depth. Then fly the camera through it.

LIVE DATA 3D CITY VIEW CAMERA CHOREOGRAPHY

The Script

This script fetches live earthquake data, maps it to a 3D city, and then runs a cinematic camera flyover. The camera starts with a bird's eye view, sweeps the skyline, does a full 360-degree orbit, dives to street level, and pulls back to an overview.

-- city_flyover.lua — Cinematic 3D flyover of live earthquake data
--
-- Fetches real-time USGS earthquake data, builds a 3D city where:
--   Height = magnitude (log scale — big quakes tower over small ones)
--   Color  = depth (shallow vs deep)
--   District = seismic network
-- Then flies the camera around the city.

ds.log("=== Earthquake City Flyover ===")
ds.log("Fetching live seismic data from USGS...")

-- Load last 30 days of earthquakes
ds.query([[
    SELECT
        round(mag, 2)        AS magnitude,
        round(depth, 1)      AS depth_km,
        net                   AS network,
        magType               AS mag_type,
        place,
        round(latitude, 2)   AS lat,
        round(longitude, 2)  AS lon,
        type                  AS event_type,
        status
    FROM read_csv_auto(
        'https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_month.csv'
    )
    WHERE mag IS NOT NULL AND mag > 0
    ORDER BY mag DESC
]])

ds.log("Loaded " .. ds.data.row_count .. " earthquakes")

-- Configure the city mapping
ds.city.height      = "magnitude"
ds.city.height_norm = "log"          -- log scale: M7 towers over M2
ds.city.color       = "depth_km"
ds.city.color_norm  = "linear"
ds.city.district    = "network"      -- group by seismic network
ds.city.label       = "place"

-- Trigger the city build (waits for completion, auto-switches to City view)
ds.city.rebuild()

-- Compute distances relative to city size
local extent = math.max(ds.city.extent_x, ds.city.extent_z)
local D = extent * 0.8   -- default fit distance
ds.log("City extent: " .. string.format("%.0f", extent) .. " units")
ds.log("")
ds.log("Starting flyover in 2 seconds...")
ds.sleep(2000)

-- =============================================
-- CINEMATIC FLYOVER
-- =============================================

-- 1. Start high up, looking down — the overview
ds.log("[camera] Bird's eye view")
ds.city.camera.set_mode("orbit")
ds.city.camera.orbit(-90, 70, D * 1.5, 1.5)
ds.city.camera.wait()
ds.sleep(1500)

-- 2. Swing around to see the skyline
ds.log("[camera] Skyline sweep")
ds.city.camera.orbit(0, 25, D, 2.0)
ds.city.camera.wait()
ds.sleep(1000)

-- 3. Continue orbiting — dramatic angle
ds.log("[camera] Dramatic angle")
ds.city.camera.orbit(90, 15, D * 0.75, 2.0)
ds.city.camera.wait()
ds.sleep(1000)

-- 4. Pull back and rise up for a wide shot
ds.log("[camera] Rising wide shot")
ds.city.camera.orbit(180, 45, D * 1.25, 2.5)
ds.city.camera.wait()
ds.sleep(1000)

-- 5. Slow 360° orbit — the hero shot
ds.log("[camera] 360° orbit")
for angle = 180, 540, 10 do
    local a = angle % 360
    if a > 180 then a = a - 360 end
    ds.city.camera.orbit(a, 30, D * 0.9, 0.15)
    ds.sleep(150)
end

-- 6. Dive low — street level view
ds.log("[camera] Street level dive")
ds.city.camera.orbit(45, 8, D * 0.4, 2.0)
ds.city.camera.wait()
ds.sleep(1500)

-- 7. Pull back to overview
ds.log("[camera] Return to overview")
ds.city.camera.fit(2.0)
ds.city.camera.wait()

ds.log("")
ds.log("=== Flyover complete ===")
ds.log("Try: ds.city.camera.orbit(0, 30, " .. string.format("%.0f", D) .. ", 1.0) to fly manually")

City Mapping

Height = magnitude (log scale), Color = depth in km, District = seismic network (groups buildings spatially).

Camera Sequence

7 shots: bird's eye, skyline sweep, dramatic angle, wide shot, 360-degree orbit, street-level dive, return to overview.

Customization

Change camera angles, speeds, and distances. Swap color to mag_type or district to event_type for different groupings.

Prerequisites

Requires internet connection for the USGS API call. Works with both free and pro versions of ColumnLens.


Run this script in ColumnLens

Paste it into the Script Console and watch the flyover.

Download on the Mac App Store
Or try the free version (up to 100 MB)