Skip to content

Commit

Permalink
Merge pull request #15 from jgaskins/add-ewkt-and-ewkb
Browse files Browse the repository at this point in the history
Add serialization to Well Known Text/Binary formats for `Geo::Coord`
  • Loading branch information
mamantoha authored Jan 6, 2025
2 parents 316468e + 2e6ed8e commit 057da62
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 0 deletions.
51 changes: 51 additions & 0 deletions spec/geo/coord_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,57 @@ describe Geo::Coord do
geojson.should be_a(GeoJSON::Coordinates)
end

describe "#to_wkt" do
it "generates a Well Known Text format" do
coord = Geo::Coord.new(50.004444, 36.231389)

ewkt = coord.to_wkt

ewkt.should eq "POINT(36.231389 50.004444)"
end
end

describe "#to_wkb" do
it "generates a Well Known Binary format" do
coord = Geo::Coord.new(12, 34)

ewkb = coord.to_wkb

ewkb.should eq Bytes[
0, # Big-Endian
0, 0, 0, 1, # POINT
0, 0, 0, 34, 0, 0, 0, 0, # Longitude encoded as IEEE-754
0, 0, 0, 12, 0, 0, 0, 0, # Latitude encoded as IEEE-754
]
end
end

describe "#to_ewkt" do
it "generates an Extended Well Known Text format" do
coord = Geo::Coord.new(50.004444, 36.231389)

ewkt = coord.to_ewkt

ewkt.should eq "SRID=4326;POINT(36.231389 50.004444)"
end
end

describe "#to_ewkb" do
it "generates an Extended Well Known Binary format" do
coord = Geo::Coord.new(12, 34)

ewkb = coord.to_ewkb

ewkb.should eq Bytes[
0, # Big-Endian
0, 0, 0, 1, # POINT
0, 0, 0, 34, 0, 0, 0, 0, # Longitude encoded as IEEE-754
0, 0, 0, 12, 0, 0, 0, 0, # Latitude encoded as IEEE-754
16, 140, # SRID 4326
]
end
end

describe "comparisons" do
describe "equality" do
pos1 = Geo::Coord.new(45.3142533036254, -93.47527313511819)
Expand Down
13 changes: 13 additions & 0 deletions spec/geo/polygon_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,19 @@ describe Geo::Polygon do
geojson.should be_a(GeoJSON::Polygon)
end

describe "#to_wkt" do
it "outputs a Well Known Text format" do
polygon = Geo::Polygon.new([
Geo::Coord.new(10, 30),
Geo::Coord.new(20, 10),
Geo::Coord.new(40, 20),
Geo::Coord.new(40, 40),
])

polygon.to_wkt.should eq "POLYGON((30 10, 10 20, 20 40, 40 40, 30 10))"
end
end

describe "comparisons" do
describe "equality" do
polygon1 = Geo::Polygon.new([pos1, pos2])
Expand Down
40 changes: 40 additions & 0 deletions src/geo/coord.cr
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,46 @@ module Geo
io << strfcoord(%{%latd°%latm'%lats"%lath %lngd°%lngm'%lngs"%lngh})
end

def to_ewkt : String
String.build { |str| to_ewkt str }
end

def to_ewkt(io : IO, output_type = true, output_parentheses = true) : Nil
# SRID 4326 is used for latitude and longitude
# https://epsg.org/crs_4326/WGS-84.html
io << "SRID=4326;"
to_wkt io, output_type: output_type, output_parentheses: output_parentheses
end

def to_ewkb(bytes : Bytes = Bytes.new(23), byte_format : IO::ByteFormat = IO::ByteFormat::BigEndian) : Bytes
to_wkb bytes, byte_format
# SRID 4326 is used for latitude and longitude
# https://epsg.org/crs_4326/WGS-84.html
byte_format.encode 4236i16, bytes + 21

bytes
end

def to_wkt : String
String.build { |str| to_wkt str }
end

def to_wkt(io : IO, output_type = true, output_parentheses = true) : Nil
io << "POINT" if output_type
io << '(' if output_parentheses
io << lng << ' ' << lat
io << ')' if output_parentheses
end

def to_wkb(bytes : Bytes = Bytes.new(21), byte_format : IO::ByteFormat = IO::ByteFormat::BigEndian) : Bytes
bytes[0] = 0 # Big Endian
byte_format.encode 1u32, bytes + 1 # POINT type
byte_format.encode lng, bytes + 5
byte_format.encode lat, bytes + 13

bytes
end

def ll
{lat, lng}
end
Expand Down
15 changes: 15 additions & 0 deletions src/geo/polygon.cr
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,21 @@ module Geo
GeoJSON::Polygon.new([coordinates])
end

def to_wkt : String
String.build { |str| to_wkt str }
end

def to_wkt(io : IO) : Nil
io << "POLYGON(("
@coords.each_with_index 1 do |coord, index|
coord.to_wkt io, output_type: false, output_parentheses: false
if index < @coords.size
io << ", "
end
end
io << "))"
end

private def calculate_centroid : Geo::Coord
centroid_lat = 0.0
centroid_lng = 0.0
Expand Down

0 comments on commit 057da62

Please sign in to comment.