GeoJSON Export Reference¶
Geodetic::GeoJSON builds a GeoJSON FeatureCollection from any combination of Geodetic objects. It provides an accumulator pattern for collecting geometry, then exporting as a Hash, JSON string, or file.
Every geometry type also gains a to_geojson instance method that returns a GeoJSON-compatible Ruby Hash.
GeoJSON Class¶
Constructor¶
gj = Geodetic::GeoJSON.new # empty collection
gj = Geodetic::GeoJSON.new(seattle, portland) # initialize with objects
gj = Geodetic::GeoJSON.new([seattle, portland]) # also accepts an array
Accumulating Objects¶
Use << to add a single object or an array of objects. Returns self for chaining.
gj = Geodetic::GeoJSON.new
gj << seattle # single coordinate
gj << [portland, sf, la] # array of coordinates
gj << Geodetic::Segment.new(a, b) # segment
gj << route # path
gj << polygon # area
gj << circle # circle (approximated)
gj << bbox # bounding box
gj << feature # feature (preserves properties)
Any Geodetic object with a to_geojson method can be added. Non-Feature objects are auto-wrapped as GeoJSON Features with empty properties. Feature objects carry their label and metadata into the GeoJSON output.
Query¶
| Method | Returns | Description |
|---|---|---|
size |
Integer | Number of collected objects |
length |
Integer | Alias for size |
empty? |
Boolean | True if no objects collected |
each |
Enumerator | Iterate over collected objects |
GeoJSON includes Enumerable, so map, select, reject, count, to_a, and all other Enumerable methods are available.
Removing Objects¶
| Method | Description |
|---|---|
delete(obj) |
Remove a specific object from the collection |
clear |
Remove all objects |
Both return self for chaining.
Export¶
| Method | Returns | Description |
|---|---|---|
to_h |
Hash | GeoJSON FeatureCollection as a Ruby Hash |
to_json |
String | Compact JSON string |
to_json(pretty: true) |
String | Pretty-printed JSON string |
save(path) |
nil | Write compact JSON to file |
save(path, pretty: true) |
nil | Write pretty-printed JSON to file |
gj.to_h
# => {"type" => "FeatureCollection", "features" => [...]}
gj.to_json
# => '{"type":"FeatureCollection","features":[...]}'
gj.to_json(pretty: true)
# => formatted JSON with indentation
gj.save("my_map.geojson")
gj.save("my_map.geojson", pretty: true)
The to_json and save methods use Ruby's built-in json library (a default gem, always available). No external dependencies are required.
Display¶
Import¶
| Method | Returns | Description |
|---|---|---|
GeoJSON.load(path) |
Array | Read a GeoJSON file and return an Array of Geodetic objects |
GeoJSON.parse(hash) |
Array | Parse a GeoJSON Hash and return an Array of Geodetic objects |
objects = Geodetic::GeoJSON.load("west_coast.geojson")
# => [Feature("Seattle", LLA), Feature("Portland", LLA), Segment, Polygon, ...]
parse accepts a Ruby Hash (useful when you already have parsed JSON):
GeoJSON → Geodetic type mapping:
| GeoJSON type | Geodetic type |
|---|---|
| Point | Coordinate::LLA |
| LineString (2 points) | Segment |
| LineString (3+ points) | Path |
| Polygon | Areas::Polygon (outer ring only; holes are dropped) |
| MultiPoint | Multiple Coordinate::LLA |
| MultiLineString | Multiple Segment or Path |
| MultiPolygon | Multiple Areas::Polygon |
| GeometryCollection | Flattened into individual geometries |
Feature handling:
- A GeoJSON Feature with a
"name"property or any non-empty properties becomes aGeodetic::Feature. The"name"property maps tolabel, and remaining properties becomemetadatawith symbol keys. - A GeoJSON Feature with empty properties (
{}) returns the raw geometry with no Feature wrapper.
This means a save/load roundtrip preserves Feature labels, metadata, and geometry types:
# Save
gj = Geodetic::GeoJSON.new
gj << Geodetic::Feature.new(label: "Seattle", geometry: seattle, metadata: { state: "WA" })
gj << portland # raw coordinate, no Feature
gj.save("cities.geojson")
# Load
objects = Geodetic::GeoJSON.load("cities.geojson")
objects[0] # => Feature (label: "Seattle", metadata: {state: "WA"})
objects[0].label # => "Seattle"
objects[0].metadata # => {state: "WA"}
objects[1] # => LLA (raw coordinate, no Feature wrapper)
Geometry Mapping¶
Each Geodetic geometry type maps to a specific GeoJSON geometry type:
| Geodetic Type | GeoJSON Type | Notes |
|---|---|---|
| Any coordinate (LLA, UTM, ECEF, ...) | Point | Converts through LLA |
Segment |
LineString | 2 positions |
Path |
LineString | N positions (default) |
Path (with as: :polygon) |
Polygon | Auto-closes the ring |
Areas::Polygon (and subclasses) |
Polygon | Boundary ring already closed |
Areas::Circle |
Polygon | Approximated as N-gon (default 32) |
Areas::BoundingBox |
Polygon | 4 corners, closed |
Feature |
Feature | Geometry + properties |
Individual to_geojson Methods¶
Coordinates¶
All 18 coordinate classes gain a to_geojson method. It converts the coordinate to LLA and returns a GeoJSON Point.
seattle = Geodetic::Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0.0)
seattle.to_geojson
# => {"type" => "Point", "coordinates" => [-122.3493, 47.6205]}
Altitude handling: The GeoJSON position array is [lng, lat] when altitude is zero, and [lng, lat, alt] when altitude is non-zero.
Geodetic::Coordinate::LLA.new(lat: 47.62, lng: -122.35, alt: 184.0).to_geojson
# => {"type" => "Point", "coordinates" => [-122.35, 47.62, 184.0]}
Cross-system: Any coordinate type works — UTM, ECEF, MGRS, GH, etc. are converted to LLA internally.
seattle.to_utm.to_geojson # => {"type" => "Point", ...}
seattle.to_mgrs.to_geojson # => {"type" => "Point", ...}
ENU and NED: These are relative coordinate systems with no absolute position. Calling to_geojson raises ArgumentError with a message explaining that conversion to an absolute system is required first.
Segment¶
Returns a GeoJSON LineString with two positions.
seg = Geodetic::Segment.new(seattle, portland)
seg.to_geojson
# => {"type" => "LineString", "coordinates" => [[-122.3493, 47.6205], [-122.6784, 45.5152]]}
Path¶
Returns a GeoJSON LineString by default. Requires at least 2 coordinates.
route = Geodetic::Path.new(coordinates: [seattle, portland, sf])
route.to_geojson
# => {"type" => "LineString", "coordinates" => [[...], [...], [...]]}
As polygon: Pass as: :polygon to export as a closed GeoJSON Polygon. Requires at least 3 coordinates. The ring is auto-closed if the first and last coordinates differ.
route.to_geojson(as: :polygon)
# => {"type" => "Polygon", "coordinates" => [[[...], [...], [...], [...]]]}
Edge cases:
| Condition | Behavior |
|---|---|
| Empty path | Raises ArgumentError |
| 1 coordinate (line_string) | Raises ArgumentError |
| 2 coordinates (polygon) | Raises ArgumentError |
Areas::Polygon¶
Returns a GeoJSON Polygon. The boundary ring is already closed by Polygon#initialize.
poly = Geodetic::Areas::Polygon.new(boundary: [a, b, c])
poly.to_geojson
# => {"type" => "Polygon", "coordinates" => [[[...], [...], [...], [...]]]}
All Polygon subclasses (Triangle, Rectangle, Pentagon, Hexagon, Octagon) inherit this method.
Areas::Circle¶
Returns a GeoJSON Polygon approximating the circle as a regular N-gon. Default is 32 segments.
circle = Geodetic::Areas::Circle.new(centroid: seattle, radius: 10_000)
circle.to_geojson # => 32-gon (33 positions including closing)
circle.to_geojson(segments: 64) # => 64-gon (65 positions including closing)
circle.to_geojson(segments: 8) # => 8-gon (9 positions including closing)
The vertices are computed using Geodetic::Vector#destination_from (Vincenty direct), so the approximation is geodetically accurate.
Areas::BoundingBox¶
Returns a GeoJSON Polygon with 4 corners plus the closing point (5 positions total). Ring order follows the GeoJSON right-hand rule: NW → NE → SE → SW → NW.
bbox = Geodetic::Areas::BoundingBox.new(
nw: Geodetic::Coordinate::LLA.new(lat: 48.0, lng: -123.0, alt: 0),
se: Geodetic::Coordinate::LLA.new(lat: 46.0, lng: -121.0, alt: 0)
)
bbox.to_geojson
# => {"type" => "Polygon", "coordinates" => [[[-123.0, 48.0], [-121.0, 48.0], [-121.0, 46.0], [-123.0, 46.0], [-123.0, 48.0]]]}
Feature¶
Returns a GeoJSON Feature. The label is mapped to the "name" property. The metadata hash is merged into properties with keys converted to strings.
f = Geodetic::Feature.new(
label: "Seattle",
geometry: seattle,
metadata: { state: "WA", population: 750_000 }
)
f.to_geojson
# => {
# "type" => "Feature",
# "geometry" => {"type" => "Point", "coordinates" => [-122.3493, 47.6205]},
# "properties" => {"name" => "Seattle", "state" => "WA", "population" => 750000}
# }
| Feature field | GeoJSON mapping |
|---|---|
label |
properties["name"] (omitted if nil) |
metadata |
Merged into properties (symbol keys stringified) |
geometry |
Delegates to the geometry's to_geojson |
The geometry can be any type: coordinate, segment, path, polygon, circle, or bounding box.
# Feature wrapping a Path
route_feature = Geodetic::Feature.new(
label: "West Coast Route",
geometry: route,
metadata: { mode: "driving" }
)
route_feature.to_geojson
# => {"type" => "Feature", "geometry" => {"type" => "LineString", ...}, "properties" => {...}}
Complete Example¶
require "geodetic"
seattle = Geodetic::Coordinate::LLA.new(lat: 47.6205, lng: -122.3493, alt: 0)
portland = Geodetic::Coordinate::LLA.new(lat: 45.5152, lng: -122.6784, alt: 0)
sf = Geodetic::Coordinate::LLA.new(lat: 37.7749, lng: -122.4194, alt: 0)
# Build a collection
gj = Geodetic::GeoJSON.new
# Add cities as features with metadata
gj << Geodetic::Feature.new(label: "Seattle", geometry: seattle, metadata: { pop: 750_000 })
gj << Geodetic::Feature.new(label: "Portland", geometry: portland, metadata: { pop: 650_000 })
gj << Geodetic::Feature.new(label: "San Francisco", geometry: sf, metadata: { pop: 870_000 })
# Add the route connecting them
route = Geodetic::Path.new(coordinates: [seattle, portland, sf])
gj << Geodetic::Feature.new(label: "West Coast Route", geometry: route)
# Add a 50km radius around Seattle
gj << Geodetic::Feature.new(
label: "Seattle Metro",
geometry: Geodetic::Areas::Circle.new(centroid: seattle, radius: 50_000)
)
# Export
gj.save("west_coast.geojson", pretty: true)
The output file can be opened directly in geojson.io, QGIS, Mapbox, Leaflet, or any other GeoJSON-compatible tool.
GeoJSON Specification Notes¶
- Coordinate order is
[longitude, latitude](not[lat, lng]), per RFC 7946 Section 3.1.1. - Altitude is optional. Included as the third element when non-zero.
- Polygon rings follow the right-hand rule: exterior rings are counterclockwise. BoundingBox uses NW → NE → SE → SW → NW.
- String keys are used throughout (
"type","coordinates","properties", etc.) per JSON convention.
Visualizing GeoJSON¶
The easiest way to verify your exported GeoJSON is geojson.io. It renders points, lines, and polygons on an interactive map with property inspection.
To use it:
- Export your collection:
gj.save("my_map.geojson", pretty: true) - Open geojson.io in a browser
- Drag and drop the
.geojsonfile onto the map, or paste the JSON into the editor panel
Feature properties (name, metadata) appear in popups when you click on a rendered geometry. This makes it a quick way to confirm that coordinates, shapes, and metadata are correct before integrating with QGIS, Mapbox, Leaflet, or other GIS tools.
- Output is a Ruby Hash. Call .to_json or JSON.generate(hash) to produce a JSON string.