Maps & globes

Latitude and longitude on a globe or a flat map, distances and circles on the Earth, GeoJSON (countries, earthquakes, routes) from a file or a URL, and a 3D point cloud to draw tens of thousands of dots at once. Angles are in degrees, distances in km.

geomapglobegeojsonlatitudelongitudepoint cloudview3d

On this page · 8 entries

Geo.ToSphere · Geo.FromSphere

  • Geo.ToSphere(lat, lon, radius) → PVector
  • Geo.FromSphere(p) → (Lat, Lon)

A place as a point on a ball centered on the origin, and back. x points east, y north (up), and longitude 0 faces -z, so a camera on the -z axis looks at Africa and Europe.

Geo.DistanceKm · Geo.Destination · Geo.Circle

  • Geo.DistanceKm(lat1, lon1, lat2, lon2)
  • Geo.Destination(lat, lon, bearing, km)
  • Geo.Circle(lat, lon, km, segments)

The great-circle distance between two places; where you end up going km along a compass bearing (0 north, 90 east); and the ring of places km away from a point — an earthquake's reach, a city's radius — ready to draw with ToSphere() or Equirectangular().

Geo.Equirectangular · Geo.FromEquirectangular

  • Geo.Equirectangular(lat, lon, w, h) → (X, Y)
  • Geo.FromEquirectangular(x, y, w, h)

The simplest flat world map: longitude -180..180 left to right, latitude 90..-90 top to bottom. Most world textures (elevation, night lights, clouds) use it, so it's also how to read one of those images at a place.

Geo.FibonacciSphere

  • Geo.FibonacciSphere(count) → PVector[]

count points spread evenly over a unit sphere — the classic dotted globe. FromSphere() gives each one's latitude and longitude.

LoadGeoJSON · RequestGeoJSON

  • LoadGeoJSON(pathOrUrl)
  • RequestGeoJSON(pathOrUrl, cacheFor) → GeoMap

A GeoJSON file (a FeatureCollection, a Feature or a bare geometry) as GeoFeatures. Each has its Points (lat, lon and the third value: altitude, or depth for earthquakes), its Rings (polygon outlines, holes marked, and lines), its properties through Text("key") and Number("key"), and Contains(lat, lon). map.FeatureAt(lat, lon) says which one a place is in — which country, with a countries file.

class View3D · View3D.Orbit · view.Project

  • class View3D
  • View3D.Orbit(yaw, pitch, distance)
  • view.Project(p, w, h) → (X, Y, Depth)

A camera for 3D on the 2D canvas: where it is, what it looks at, how wide it sees. Project() gives a point's screen position and depth (0 or less: behind the camera) — for drawing anything there, placing a label or hit-testing the mouse. x right, y up, z away from you, the same axes as Geo.ToSphere().

class PointCloud · cloud.Add · Points

  • class PointCloud
  • cloud.Add(x, y, z, color, size)
  • Points(cloud, view)

Many colored points drawn in a single batch: tens of thousands at the cost of a few hundred Circle() calls. Sorted far to near by default; set OccluderCenter/OccluderRadius to hide the ones behind a ball (the far side of a globe). Sizes are in world units unless SizeInWorld = false.

Runnable example

A complete sketch using this section. Test in Dana opens it in the IDE, or paste it into a new tab and press Run.

C#
// A dotted globe: 20,000 points in one batch, a few cities, and the
// 1,000 km around the one under the mouse. Drag to turn it.
public class Globe : Sketch
{
    readonly PointCloud cloud = new();
    readonly PVector[] dots = Geo.FibonacciSphere(20000);
    float yaw = -0.3f, pitch = 0.4f;
    (string Name, float Lat, float Lon)[] cities =
        { ("Havana", 23.1f, -82.4f), ("Madrid", 40.4f, -3.7f), ("Lagos", 6.5f, 3.4f), ("Cairo", 30.0f, 31.2f) };

    public override void Setup()
    {
        Size(700, 500);
        cloud.OccluderRadius = 9.9f;          // hide the far side of the ball
    }

    public override void Draw()
    {
        Background(8, 12, 24);
        var view = View3D.Orbit(yaw, pitch, 28);
        cloud.Clear();
        foreach (var d in dots)
        {
            var (lat, lon) = Geo.FromSphere(d);
            bool grid = Abs(lat % 30) < 1 || Abs(lon % 30) < 1;
            cloud.Add(PVector.Mult(d, 10), grid ? Color(90, 160, 220) : Color(40, 60, 100), 0.08f);
        }
        Points(cloud, view);

        foreach (var c in cities)
        {
            var (x, y, depth) = view.Project(Geo.ToSphere(c.Lat, c.Lon, 10), Width, Height);
            if (depth > 28) continue;         // behind the globe
            bool near = Dist(MouseX, MouseY, x, y) < 12;
            if (near)
            {
                NoFill();
                Stroke(255, 200, 80);
                BeginShape();
                foreach (var (la, lo) in Geo.Circle(c.Lat, c.Lon, 1000))
                {
                    var p = view.Project(Geo.ToSphere(la, lo, 10.02f), Width, Height);
                    Vertex(p.X, p.Y);
                }
                EndShape();
            }
            NoStroke();
            Fill(255, 200, 80);
            Circle(x, y, 8);
            Fill(255);
            Text($"{c.Name}  ({Geo.DistanceKm(c.Lat, c.Lon, 23.1f, -82.4f):N0} km from Havana)", x + 8, y - 8);
        }
    }

    public override void MouseDragged()
    {
        yaw -= (MouseX - PMouseX) * 0.01f;
        pitch = Constrain(pitch + (MouseY - PMouseY) * 0.01f, -1.4f, 1.4f);
    }
}