Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
mamantoha committed Nov 8, 2019
0 parents commit 3ef15ae
Show file tree
Hide file tree
Showing 9 changed files with 318 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*.cr]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/docs/
/lib/
/bin/
/.shards/
*.dwarf

# Libraries don't need dependency lock
# Dependencies will be locked in applications that use them
/shard.lock
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
language: crystal

# Uncomment the following if you'd like Travis to run specs and check code formatting
# script:
# - crystal spec
# - crystal tool format --check
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2019 Anton Maminov

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Geo::Coord

Geo Coordinates class for Crystal, inspired by the Ruby's [geo_coord](https://github.com/zverok/geo_coord) gem.

## Installation

1. Add the dependency to your `shard.yml`:

```yaml
dependencies:
geo_coord:
github: mamantoha/geo_coord
```
2. Run `shards install`

## Usage

```crystal
require "geo_coord"
c = Geo::Coord.new(50.004444, 36.231389)
c.to_s
# => "50°0'16\"N 36°13'53\"E"
c.to_s(dms: false)
# => "50.004444,36.231389"
c.strfcoord(%{%latd %latm' %lats" %lath, %lngd %lngm' %lngs" %lngh})
# => "50°0'16\"N 36°13'53\"E"
```

## Contributing

1. Fork it (<https://github.com/mamantoha/geo_coord/fork>)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request

## Contributors

- [Anton Maminov](https://github.com/mamantoha) - creator and maintainer
9 changes: 9 additions & 0 deletions shard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name: geo_coord
version: 0.1.0

authors:
- Anton Maminov <[email protected]>

crystal: 0.31.1

license: MIT
75 changes: 75 additions & 0 deletions spec/geo_coord_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
require "./spec_helper"

describe Geo::Coord do
context "initialize" do
it "is initialized by (lat, lng)" do
c = Geo::Coord.new(50.004444, 36.231389)

c.lat.should eq(50.004444)
c.lng.should eq(36.231389)
end
end

context "strfcoord" do
it "renders components" do
pos = Geo::Coord.new(50.004444, 36.231389)
neg = Geo::Coord.new(-50.004444, -36.231389)

pos.strfcoord("%latd").should eq("50")
neg.strfcoord("%latd").should eq("50")
neg.strfcoord("%latds").should eq("-50")

pos.strfcoord("%latm").should eq("0")
pos.strfcoord("%lats").should eq("16")
pos.strfcoord("%lath").should eq("N")
neg.strfcoord("%lath").should eq("S")

pos.strfcoord("%lat").should eq("%f" % pos.lat)
neg.strfcoord("%lat").should eq("%f" % neg.lat)

pos.strfcoord("%lngd").should eq("36")
neg.strfcoord("%lngd").should eq("36")
neg.strfcoord("%lngds").should eq("-36")

pos.strfcoord("%lngm").should eq("13")
pos.strfcoord("%lngs").should eq("53")
pos.strfcoord("%lngh").should eq("E")
neg.strfcoord("%lngh").should eq("W")

pos.strfcoord("%lng").should eq("%f" % pos.lng)
neg.strfcoord("%lng").should eq("%f" % neg.lng)
end

it "just leaves unknown parts" do
pos = Geo::Coord.new(50.004444, 36.231389)
pos.strfcoord("%latd %foo").should eq("50 %foo")
end

it "understands everyting at once" do
pos = Geo::Coord.new(50.004444, 36.231389)

pos.strfcoord(%{%latd %latm' %lats" %lath, %lngd %lngm' %lngs" %lngh})
.should eq(%{50 0' 16" N, 36 13' 53" E})
end

it "can carry" do
pos = Geo::Coord.new(0.033333, 91.333333)

pos.strfcoord("%latd %latm %lats, %lngd %lngm %lngs").should eq("0 2 0, 91 20 0")
pos.strfcoord("%latd %latm %0.2lats, %lngd %lngm %0.2lngs").should eq("0 2 0.00, 91 20 0.00")
pos.strfcoord("%latd %latm %0.3lats, %lngd %lngm %0.3lngs").should eq("0 1 59.999, 91 19 59.999")
end
end

context "to_s" do
it "is convertible to string" do
c = Geo::Coord.new(50.004444, 36.231389)
c.to_s.should eq(%{50°0'16"N 36°13'53"E})
c.to_s(dms: false).should eq("50.004444,36.231389")

c = Geo::Coord.new(-50.004444, -36.231389)
c.to_s.should eq(%{50°0'16"S 36°13'53"W})
c.to_s(dms: false).should eq("-50.004444,-36.231389")
end
end
end
2 changes: 2 additions & 0 deletions spec/spec_helper.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require "spec"
require "../src/geo_coord"
143 changes: 143 additions & 0 deletions src/geo_coord.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
module Geo
class Coord
VERSION = "0.1.0"

getter :lat
getter :lng

INTFLAGS = /\+?/
FLOATUFLAGS = /0\.\d+/
FLOATFLAGS = /\+?#{FLOATUFLAGS}?/

DIRECTIVES = {
# Latitude
/%(#{FLOATUFLAGS})?lats/ => ->(m : Regex::MatchData) { "%<lats>#{m[1]? || "0."}f" },
"%latm" => "%<latm>i",
/%(#{INTFLAGS})?latds/ => ->(m : Regex::MatchData) { "%<latds>#{m[1]}i" },
"%latd" => "%<latd>i",
"%lath" => "%<lath>s",
/%(#{FLOATFLAGS})?lat/ => ->(m : Regex::MatchData) { "%<lat>#{m[1]}f" },
# Longitude
/%(#{FLOATUFLAGS})?lngs/ => ->(m : Regex::MatchData) { "%<lngs>#{m[1]? || "0."}f" },
"%lngm" => "%<lngm>i",
/%(#{INTFLAGS})?lngds/ => ->(m : Regex::MatchData) { "%<lngds>#{m[1]}i" },
"%lngd" => "%<lngd>i",
"%lngh" => "%<lngh>s",
/%(#{FLOATFLAGS})?lng/ => ->(m : Regex::MatchData) { "%<lng>#{m[1]}f" },
}

def initialize(@lat : Float32 | Float64, @lng : Float32 | Float64)
end

# Returns latitude degrees
def latd : Int32
lat.abs.to_i
end

# Returns latitude minutes
def latm : Int32
(lat.abs * 60).to_i % 60
end

# Returns latitude seconds
def lats : Float32 | Float64
(lat.abs * 3600) % 60
end

# Returns latitude hemisphere
def lath : String
lat > 0 ? "N" : "S"
end

# Returns longitude degrees
def lngd : Int32
lng.abs.to_i
end

# Returns longitude minutes
def lngm : Int32
(lng.abs * 60).to_i % 60
end

# Returns longitude seconds
def lngs : Float32 | Float64
(lng.abs * 3600) % 60
end

# Returns longitude hemisphere
def lngh : String
lng > 0 ? "E" : "W"
end

def latds
lat.to_i
end

def lngds
lng.to_i
end

def strfcoord(formatstr)
h = full_hash

DIRECTIVES.reduce(formatstr) do |memo, (from, to)|
memo.gsub(from) do
to = to.call($~) if to.is_a?(Proc)

res = to % h

if tmp = guard_seconds(to, res)
res, carrymin = tmp

unless carrymin.empty?
if h[carrymin].is_a?(Int32)
tmp = h[carrymin].as(Int32)
h[carrymin] = tmp + 1
end
end
end

res
end
end
end

# Returns a string representing coordinates.
#
# ```
# g.to_s # => "50°0'16\"N 36°13'53\"E"
# g.to_s(dms: false) # => "50.004444,36.231389"
# ```
def to_s(dms : Bool = true)
format = dms ? %{%latd°%latm'%lats"%lath %lngd°%lngm'%lngs"%lngh} : "%lat,%lng"
strfcoord(format)
end

private def guard_seconds(pattern : String, result : String) : Array(String)?
if m = pattern.match(/<(lat|lng)s>/)
return [result, ""] unless m && result.starts_with?("60")
carry = "#{m[1]}m"
[pattern % {"lats" => 0, "lngs" => 0}, carry]
end
end

private def full_hash : Hash(String, Int32 | Float32 | Float64 | String)
{
# Latitude
"latd" => latd,
"latds" => latds,
"latm" => latm,
"lats" => lats,
"lath" => lath,
"lat" => lat,
# Longitude
"lngd" => lngd,
"lngds" => lngds,
"lngm" => lngm,
"lngs" => lngs,
"lngh" => lngh,
"lng" => lng,
}
end
end
end

0 comments on commit 3ef15ae

Please sign in to comment.