geoq 0.0.1.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f9190180a02062cf1119badc7fd43d908bd1e5ba
4
+ data.tar.gz: ad237a21af279b4fd18e1dd0950f81e7986919da
5
+ SHA512:
6
+ metadata.gz: b3a19251027bf22b2696c5286cd797fa3c4e36725969259619be913a06012469a431df93e6d6dc9af4fa94051ba9b2510d9f14689b51753dd33b6b25547d9345
7
+ data.tar.gz: 38c00b909152f112bc6e7de00fd971e553e9dc44f33b9ae965b7201b4a6f72b1af7aa8bf22cbb55e3ddef53a226aa55b762cb92741923ae66132faa5fc960143
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,85 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ geoq (0.0.1)
5
+ gli (~> 2.17)
6
+ pr_geohash (~> 1.0)
7
+ rgeo (~> 1.0)
8
+ rgeo-geojson (~> 2.0)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ ansi (1.5.0)
14
+ aruba (0.14.5)
15
+ childprocess (>= 0.6.3, < 0.10.0)
16
+ contracts (~> 0.9)
17
+ cucumber (>= 1.3.19)
18
+ ffi (~> 1.9.10)
19
+ rspec-expectations (>= 2.99)
20
+ thor (~> 0.19)
21
+ backports (3.11.3)
22
+ builder (3.2.3)
23
+ childprocess (0.9.0)
24
+ ffi (~> 1.0, >= 1.0.11)
25
+ coderay (1.1.2)
26
+ contracts (0.16.0)
27
+ cucumber (3.1.0)
28
+ builder (>= 2.1.2)
29
+ cucumber-core (~> 3.1.0)
30
+ cucumber-expressions (~> 5.0.4)
31
+ cucumber-wire (~> 0.0.1)
32
+ diff-lcs (~> 1.3)
33
+ gherkin (~> 5.0)
34
+ multi_json (>= 1.7.5, < 2.0)
35
+ multi_test (>= 0.1.2)
36
+ cucumber-core (3.1.0)
37
+ backports (>= 3.8.0)
38
+ cucumber-tag_expressions (~> 1.1.0)
39
+ gherkin (>= 5.0.0)
40
+ cucumber-expressions (5.0.17)
41
+ cucumber-tag_expressions (1.1.1)
42
+ cucumber-wire (0.0.1)
43
+ diff-lcs (1.3)
44
+ ffi (1.9.23)
45
+ gherkin (5.0.0)
46
+ gli (2.17.1)
47
+ method_source (0.9.0)
48
+ minitest (5.11.3)
49
+ minitest-reporters (1.2.0)
50
+ ansi
51
+ builder
52
+ minitest (>= 5.0)
53
+ ruby-progressbar
54
+ multi_json (1.13.1)
55
+ multi_test (0.1.2)
56
+ pr_geohash (1.0.0)
57
+ pry (0.11.3)
58
+ coderay (~> 1.1.0)
59
+ method_source (~> 0.9.0)
60
+ rake (12.3.1)
61
+ rdoc (6.0.4)
62
+ rgeo (1.0.0)
63
+ rgeo-geojson (2.0.0)
64
+ rgeo (~> 1.0)
65
+ rspec-expectations (3.7.0)
66
+ diff-lcs (>= 1.2.0, < 2.0)
67
+ rspec-support (~> 3.7.0)
68
+ rspec-support (3.7.1)
69
+ ruby-progressbar (1.9.0)
70
+ thor (0.20.0)
71
+
72
+ PLATFORMS
73
+ ruby
74
+
75
+ DEPENDENCIES
76
+ aruba (~> 0.14)
77
+ geoq!
78
+ minitest (~> 5.11)
79
+ minitest-reporters (~> 1.2)
80
+ pry (~> 0.11)
81
+ rake (~> 12.3)
82
+ rdoc (~> 6.0)
83
+
84
+ BUNDLED WITH
85
+ 1.16.2
@@ -0,0 +1,55 @@
1
+ # geoq
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/geoq.svg)](https://badge.fury.io/rb/geoq)
4
+
5
+ [![Build Status](https://travis-ci.org/worace/geoq.svg?branch=master)](https://travis-ci.org/worace/geoq)
6
+
7
+ Geospatial Utility Belt.
8
+
9
+ ## Install
10
+
11
+ ```
12
+ gem install geoq
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ **[View the Usage Doc](https://github.com/worace/geoq/blob/master/usage.md)** for detailed usage examples.
18
+
19
+ To view the included help just run the base command:
20
+
21
+ ```
22
+ geoq
23
+ # or
24
+ geoq --help
25
+ ```
26
+
27
+ Or view help for a specific command:
28
+
29
+ ```
30
+ geoq wkt --help
31
+ ```
32
+
33
+ ## Feature Wishlist
34
+
35
+ * [X] Read WKT
36
+ * [X] Read GeoJSON
37
+ * [X] Read Geohash
38
+ * [X] Read Lat,Lon
39
+ * [X] Output GeoJSON geom
40
+ * [X] Output GeoJSON feature
41
+ * [X] Output GeoJSON featurecollection
42
+ * [X] Output geohash (point)
43
+ * [ ] Output geohashes (geometry -- covering geohashes)
44
+ * [ ] Output Geom Centroid
45
+ * [ ] Output Geom area
46
+ * [ ] Output Geom perimeter
47
+ * [X] Output Geohash Children
48
+ * [X] Output Geohash Neighbors
49
+ * [ ] Output results to gist (geojson only)
50
+ * [X] Literate-style Usage doc with checked examples
51
+
52
+ ## Releasing
53
+
54
+ ```
55
+ ```
@@ -0,0 +1,6 @@
1
+ = geoq
2
+
3
+ Describe your project here
4
+
5
+ :include:geoq.rdoc
6
+
@@ -0,0 +1,45 @@
1
+ require 'rake/clean'
2
+ require 'rubygems'
3
+ require 'rubygems/package_task'
4
+ require 'rdoc/task'
5
+ require 'cucumber'
6
+ require 'cucumber/rake/task'
7
+ Rake::RDocTask.new do |rd|
8
+ rd.main = "README.rdoc"
9
+ rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
10
+ rd.title = 'Your application title'
11
+ end
12
+
13
+ spec = eval(File.read('geoq.gemspec'))
14
+
15
+ Gem::PackageTask.new(spec) do |pkg|
16
+ end
17
+ CUKE_RESULTS = 'results.html'
18
+ CLEAN << CUKE_RESULTS
19
+ desc 'Run features'
20
+ Cucumber::Rake::Task.new(:features) do |t|
21
+ opts = "features --format html -o #{CUKE_RESULTS} --format progress -x"
22
+ opts += " --tags #{ENV['TAGS']}" if ENV['TAGS']
23
+ t.cucumber_opts = opts
24
+ t.fork = false
25
+ end
26
+
27
+ desc 'Run features tagged as work-in-progress (@wip)'
28
+ Cucumber::Rake::Task.new('features:wip') do |t|
29
+ tag_opts = ' --tags ~@pending'
30
+ tag_opts = ' --tags @wip'
31
+ t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty -x -s#{tag_opts}"
32
+ t.fork = false
33
+ end
34
+
35
+ task :cucumber => :features
36
+ task 'cucumber:wip' => 'features:wip'
37
+ task :wip => 'features:wip'
38
+ require 'rake/testtask'
39
+ Rake::TestTask.new(:test) do |t|
40
+ t.libs << "test"
41
+ t.libs << "lib"
42
+ t.test_files = FileList["test/**/*_test.rb"]
43
+ end
44
+
45
+ task :default => [:test]
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env ruby
2
+ require 'gli'
3
+ begin # XXX: Remove this begin/rescue before distributing your app
4
+ require 'geoq'
5
+ rescue LoadError
6
+ STDERR.puts "In development, you need to use `bundle exec bin/geoq` to run your app"
7
+ STDERR.puts "At install-time, RubyGems will make sure lib, etc. are in the load path"
8
+ STDERR.puts "Feel free to remove this message from bin/geoq now"
9
+ exit 64
10
+ end
11
+
12
+ include GLI::App
13
+
14
+ program_desc <<-desc
15
+ Geospatial utility belt
16
+
17
+ geoq is a command-line tool for converting between various
18
+ GIS serialization formats. For example:
19
+
20
+ echo 9q5 | geoq gj geometry
21
+
22
+ Will output the geohash 9q5 as a geojson geometry.
23
+
24
+ Most commands will accept a Lat/Lon (comma-separated), a Geohash
25
+ (base 32 encoded), a WKT, or a GeoJSON input.
26
+
27
+ The tool should recognize the input type based on its format.
28
+
29
+ Commands expect one geo entity per line.
30
+
31
+ See COMMANDS for more possible commands.
32
+
33
+ See geoq <COMMAND> --help for more info on a given command.
34
+ desc
35
+
36
+ version Geoq::VERSION
37
+
38
+ subcommand_option_handling :normal
39
+ arguments :strict
40
+
41
+
42
+ desc 'Convert to and manipulate GeoJSON.'
43
+ # desc 'Treat all lines of input as a single collection'
44
+ # switch [:s,:slurp]
45
+ command :gj do |c|
46
+ c.desc "Output entity as a geojson geometry"
47
+ c.command :geometry do |c|
48
+ c.action do |global_options,options,args|
49
+ Geoq::GeomReader.new(STDIN).each do |entity|
50
+ puts entity.to_geojson
51
+ end
52
+ end
53
+ end
54
+
55
+ c.desc "Output entity as a geojson feature"
56
+ c.command :feature do |c|
57
+ c.action do |global_options,options,args|
58
+ Geoq::GeomReader.new(STDIN).each do |entity|
59
+ puts entity.to_geojson(true)
60
+ end
61
+ end
62
+ end
63
+
64
+ c.desc "Combine entities into a geojson feature collection"
65
+ c.command :fc do |c|
66
+ c.action do |global_options,options,args|
67
+ entities = Geoq::GeomReader.new(STDIN)
68
+ puts Geoq::Commands::GeoJson::FeatureCollection.new(entities).output
69
+ end
70
+ end
71
+ end
72
+
73
+ desc "Output entity as WKT"
74
+ command :wkt do |c|
75
+ c.action do |global_options,options,args|
76
+ Geoq::GeomReader.new(STDIN).each do |geom|
77
+ puts geom.to_wkt
78
+ end
79
+ end
80
+ end
81
+
82
+ desc "Encode and manipulate geometries as GeoHashes"
83
+ command :gh do |c|
84
+ c.desc "Encode supplied point as a GeoHash at the specified level. Only supports lat,lon points."
85
+ c.arg "level", required: true
86
+ c.command :point do |c|
87
+ c.action do |global_options,options,args|
88
+ reader = Geoq::GeomReader.new(STDIN)
89
+ Geoq::Commands::GeoHash::Point.new(reader, global_options, options, args).output.each do |gh|
90
+ puts gh
91
+ end
92
+ end
93
+ end
94
+
95
+ c.desc "Output the 32 children of the supplied Geohash."
96
+ c.command :children do |c|
97
+ c.action do |global_options,options,args|
98
+ reader = Geoq::GeomReader.new(STDIN)
99
+ Geoq::Commands::GeoHash::Children.new(reader, global_options, options, args).output.each do |gh|
100
+ puts gh
101
+ end
102
+ end
103
+ end
104
+
105
+ c.desc "Output the 8 neighbors of the supplied Geohash."
106
+ c.command :neighbors do |c|
107
+ c.switch :i, "inclusive",
108
+ desc: "Include the supplied geohash in the output, giving a 3x3 grid centered on the geohash. Default: False",
109
+ negatable: false
110
+ c.action do |global_options,options,args|
111
+ reader = Geoq::GeomReader.new(STDIN)
112
+ Geoq::Commands::GeoHash::Neighbors.new(reader, global_options, options, args).output.each do |gh|
113
+ puts gh
114
+ end
115
+ end
116
+ end
117
+
118
+ end
119
+
120
+ pre do |global,command,options,args|
121
+ # Pre logic here
122
+ # Return true to proceed; false to abort and not call the
123
+ # chosen command
124
+ # Use skips_pre before a command to skip this block
125
+ # on that command only
126
+ true
127
+ end
128
+
129
+ post do |global,command,options,args|
130
+ # Post logic here
131
+ # Use skips_post before a command to skip this
132
+ # block on that command only
133
+ end
134
+
135
+ on_error do |exception|
136
+ # Error logic here
137
+ # return false to skip default error handling
138
+ true
139
+ end
140
+
141
+ exit run(ARGV)
@@ -0,0 +1,29 @@
1
+ # Ensure we require the local version and not one we might have installed already
2
+ require File.join([File.dirname(__FILE__),'lib','geoq','version.rb'])
3
+ spec = Gem::Specification.new do |s|
4
+ s.name = 'geoq'
5
+ s.version = Geoq::VERSION
6
+ s.author = 'Horace Williams'
7
+ s.email = 'horace@worace.works'
8
+ s.homepage = 'https://github.com/worace/geoq'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.license = 'MIT'
11
+ s.summary = 'geoq is a command-line utility for GIS format conversion and common GIS operations'
12
+ s.files = `git ls-files`.split('
13
+ ')
14
+ s.require_paths << 'lib'
15
+ s.has_rdoc = false
16
+ s.bindir = 'bin'
17
+ s.executables << 'geoq'
18
+
19
+ s.add_development_dependency('rake', '~>12.3')
20
+ s.add_development_dependency('aruba', '~>0.14')
21
+ s.add_development_dependency('minitest', '~> 5.11')
22
+ s.add_development_dependency('minitest-reporters', '~>1.2')
23
+ s.add_development_dependency('pry', '~>0.11')
24
+
25
+ s.add_runtime_dependency('gli','~>2.17')
26
+ s.add_runtime_dependency('rgeo', '~>1.0')
27
+ s.add_runtime_dependency('rgeo-geojson', '~>2.0')
28
+ s.add_runtime_dependency('pr_geohash', '~>1.0')
29
+ end
@@ -0,0 +1,7 @@
1
+ require 'geoq/version.rb'
2
+ require 'geoq/geom_reader.rb'
3
+ require 'geoq/commands/base.rb'
4
+ require 'geoq/commands/geohash.rb'
5
+ require 'geoq/commands/geojson.rb'
6
+ require 'geoq/entity.rb'
7
+ require 'geoq/errors.rb'
@@ -0,0 +1,17 @@
1
+ module Geoq
2
+ module Commands
3
+ class Base
4
+ attr_reader :global_opts, :opts, :args, :instream
5
+ def initialize(instream, global_opts = {}, opts = {}, args = [])
6
+ @global_opts = global_opts
7
+ @opts = opts
8
+ @args = args
9
+ @instream = instream
10
+ end
11
+
12
+ def output
13
+ raise "Not implemented"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+ module Geoq
2
+ module Commands
3
+ module GeoHash
4
+ class Point < Base
5
+ def level
6
+ args.first.to_i
7
+ end
8
+
9
+ def output
10
+ Enumerator.new do |e|
11
+ instream.each do |entity|
12
+ e << entity.gh_string(level)
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ class Children < Base
19
+ def output
20
+ Enumerator.new do |e|
21
+ instream.each do |entity|
22
+ entity.gh_children.each do |gh|
23
+ e << gh
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ class Neighbors < Base
31
+ def output
32
+ Enumerator.new do |e|
33
+ instream.each do |entity|
34
+ entity.gh_neighbors(opts[:inclusive]).each do |gh|
35
+ e << gh
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,11 @@
1
+ module Geoq
2
+ module Commands
3
+ module GeoJson
4
+ class FeatureCollection < Base
5
+ def output
6
+ Geoq::FeatureCollection.new(instream).to_geojson
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,97 @@
1
+ require "rgeo/geo_json"
2
+ require "pr_geohash"
3
+ require "rgeo"
4
+
5
+ module Geoq
6
+ class Entity
7
+ attr_reader :entity, :raw
8
+
9
+ def initialize(entity, raw)
10
+ @entity = entity
11
+ @raw = raw
12
+ end
13
+
14
+ def as_geojson(feature = false)
15
+ geom = RGeo::GeoJSON.encode(entity)
16
+ if feature
17
+ {type: "Feature",
18
+ properties: {},
19
+ geometry: geom}
20
+ else
21
+ geom
22
+ end
23
+ end
24
+
25
+ def to_geojson(feature = false)
26
+ as_geojson(feature).to_json
27
+ end
28
+
29
+ def to_wkt
30
+ entity.as_text
31
+ end
32
+
33
+ def gh_string(level)
34
+ if entity.dimension == 0
35
+ GeoHash.encode(entity.y, entity.x, level)
36
+ else
37
+ raise RepresentationError.new("GeoHash representation not supported for #{entity.to_s}")
38
+ end
39
+ end
40
+
41
+ def gh_children
42
+ raise RepresentationError.new("GeoHash children not supported for #{entity.to_s}")
43
+ end
44
+
45
+ def gh_neighbors(inclusive = false)
46
+ raise RepresentationError.new("GeoHash neighbors not supported for #{entity.to_s}")
47
+ end
48
+ end
49
+
50
+ class Geohash < Entity
51
+ def gh_children
52
+ BASE_32.chars.map { |char| raw + char }
53
+ end
54
+
55
+ def gh_neighbors(inclusive = false)
56
+ if inclusive
57
+ [raw] + GeoHash.neighbors(raw)
58
+ else
59
+ GeoHash.neighbors(raw)
60
+ end
61
+ end
62
+ end
63
+
64
+ class Wkt < Entity
65
+ end
66
+
67
+ class LatLon < Entity
68
+ end
69
+
70
+ class GeoJson < Entity
71
+ def as_geojson(feature = false)
72
+ if feature
73
+ if entity.is_a?(RGeo::GeoJSON::Feature)
74
+ RGeo::GeoJSON.encode(entity)
75
+ else
76
+ {type: "Feature",
77
+ properties: {},
78
+ geometry: RGeo::GeoJSON.encode(entity)}
79
+ end
80
+ else
81
+ RGeo::GeoJSON.encode(entity)
82
+ end
83
+ end
84
+ end
85
+
86
+ class FeatureCollection
87
+ attr_reader :entities
88
+ def initialize(entities)
89
+ @entities = entities
90
+ end
91
+
92
+ def to_geojson
93
+ {type: "FeatureCollection",
94
+ features: entities.map { |e| e.as_geojson(true) } }.to_json
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,4 @@
1
+ module Geoq
2
+ class RepresentationError < ArgumentError
3
+ end
4
+ end
@@ -0,0 +1,71 @@
1
+ require "pr_geohash"
2
+ require "rgeo"
3
+ require "rgeo/geo_json"
4
+
5
+ module Geoq
6
+ BASE_32 = %w(0 1 2 3 4 5 6 7 8 9 b c d e f g h j
7
+ k m n p q r s t u v w x y z).join("")
8
+ class GeomReader
9
+ attr_reader :wkt
10
+
11
+ GH_REGEX = Regexp.new(/\A[#{BASE_32}]+\z/)
12
+
13
+ LAT_LON_REGEX = /\A-?\d+\.?\d*,-?\d+\.?\d*\z/
14
+
15
+ include Enumerable
16
+
17
+ def initialize(instream)
18
+ @instream = instream
19
+ @wkt = RGeo::WKRep::WKTParser.new
20
+ @factory = RGeo::Cartesian.factory
21
+ end
22
+
23
+ def each(&block)
24
+ instream.each_line do |l|
25
+ block.call(decode(l))
26
+ end
27
+ end
28
+
29
+ def decode(line)
30
+ if geohash?(line)
31
+ (lat1, lon1), (lat2, lon2) = GeoHash.decode(line)
32
+ p1 = factory.point(lon1, lat1)
33
+ p2 = factory.point(lon2, lat2)
34
+ geom = RGeo::Cartesian::BoundingBox.create_from_points(p1, p2).to_geometry
35
+ Geohash.new(geom, strip_whitespace(line))
36
+ elsif geojson?(line)
37
+ GeoJson.new(RGeo::GeoJSON.decode(line), line)
38
+ elsif latlon?(line)
39
+ LatLon.new(factory.point(*strip_whitespace(line).split(",").map(&:to_f).reverse), line)
40
+ else
41
+ Wkt.new(wkt.parse(line), line)
42
+ end
43
+ end
44
+
45
+ def strip_whitespace(line)
46
+ line.gsub(/\s+/, "").downcase
47
+ end
48
+
49
+ def geohash?(line)
50
+ !!GH_REGEX.match(strip_whitespace(line))
51
+ end
52
+
53
+ def geojson?(line)
54
+ line.lstrip.start_with?("{")
55
+ end
56
+
57
+ def latlon?(line)
58
+ !!LAT_LON_REGEX.match(strip_whitespace(line))
59
+ end
60
+
61
+ private
62
+
63
+ def instream
64
+ @instream
65
+ end
66
+
67
+ def factory
68
+ @factory
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,3 @@
1
+ module Geoq
2
+ VERSION = '0.0.1.pre'
3
+ end
@@ -0,0 +1,78 @@
1
+ require "test_helper"
2
+ require "json"
3
+
4
+ module Geoq
5
+ class CommandsTest < Minitest::Test
6
+ def test_feature_collection
7
+ fc = Geoq::Commands::GeoJson::FeatureCollection.new(TestData.mixed_stream)
8
+ json = JSON.parse(fc.output)
9
+ assert_equal "FeatureCollection", json["type"]
10
+ assert_equal ["type", "features"].sort, json.keys.sort
11
+ features = json["features"]
12
+ assert_equal 5, features.count
13
+
14
+ # encodes properties
15
+ assert_equal [{}, {}, {}, {"a" => "b"}, {}], features.map { |f| f["properties"] }
16
+ assert_equal Hash.new, features[0]["properties"]
17
+ assert_equal Hash.new, features[1]["properties"]
18
+ assert_equal Hash.new, features[2]["properties"]
19
+ assert_equal({"a" => "b"}, features[3]["properties"])
20
+
21
+ assert_equal ["Polygon", "Point", "Point", "Point", "Point"], features.map { |f| f["geometry"]["type"] }
22
+
23
+ gh = [[[-119.53125, 33.75], [-118.125, 33.75], [-118.125, 35.15625], [-119.53125, 35.15625], [-119.53125, 33.75]]]
24
+ assert_equal [gh, [0.0,1.0], [1.0, 2.0], [3.0, 4.0], [-118.3, 34.52]], features.map { |f| f["geometry"]["coordinates"] }
25
+ end
26
+
27
+ def test_geohash_point
28
+ input = TestData.stream(["0,0", "34, -118"])
29
+ command = Geoq::Commands::GeoHash::Point.new(input, {}, {}, ["4"])
30
+ assert_equal ["7zzz", "9qh1"], command.output.to_a
31
+ end
32
+
33
+ def test_gh_point_raises_for_geometry
34
+ input = TestData.mixed_stream.take(1)
35
+ command = Geoq::Commands::GeoHash::Point.new(input, {}, {}, ["4"])
36
+ assert_raises Geoq::RepresentationError do
37
+ command.output.to_a
38
+ end
39
+ end
40
+
41
+ def test_geohash_children
42
+ input = TestData.stream(["9q"])
43
+ command = Geoq::Commands::GeoHash::Children.new(input)
44
+ children = ["9q0", "9q1", "9q2", "9q3", "9q4", "9q5", "9q6",
45
+ "9q7", "9q8", "9q9", "9qb", "9qc", "9qd", "9qe",
46
+ "9qf", "9qg", "9qh", "9qj", "9qk", "9qm", "9qn",
47
+ "9qp", "9qq", "9qr", "9qs", "9qt", "9qu", "9qv",
48
+ "9qw", "9qx", "9qy", "9qz"]
49
+ assert_equal children, command.output.to_a
50
+ end
51
+
52
+ def test_geohash_children_raises_for_other_geoms
53
+ TestData.mixed_stream.reject do |entity|
54
+ entity.is_a?(Geohash)
55
+ end.each do |i|
56
+ s = TestData.stream([i.raw])
57
+ command = Geoq::Commands::GeoHash::Children.new(s)
58
+ assert_raises Geoq::RepresentationError do
59
+ command.output.to_a
60
+ end
61
+ end
62
+ end
63
+
64
+ def test_geohash_neighbors
65
+ input = TestData.stream(["9q"])
66
+ command = Geoq::Commands::GeoHash::Neighbors.new(input)
67
+ neighbors = ["9r", "9x", "9w", "9t", "9m", "9j", "9n", "9p"]
68
+ assert_equal neighbors, command.output.to_a
69
+ end
70
+
71
+ def test_geohash_neighbors_inclusive
72
+ input = TestData.stream(["9q"])
73
+ command = Geoq::Commands::GeoHash::Neighbors.new(input, {}, {inclusive: true})
74
+ neighbors = ["9q", "9r", "9x", "9w", "9t", "9m", "9j", "9n", "9p"]
75
+ assert_equal neighbors, command.output.to_a
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,56 @@
1
+ require "test_helper"
2
+ require "json"
3
+
4
+ module Geoq
5
+ class GeomReaderTest < Minitest::Test
6
+ attr_reader :reader
7
+
8
+ def setup
9
+ r,w = IO.pipe
10
+ w.close
11
+ @reader = Geoq::GeomReader.new(r)
12
+ end
13
+
14
+ def test_recognizes_geohashes
15
+ assert reader.geohash?("9q5")
16
+ assert reader.geohash?("9Q5")
17
+ assert reader.geohash?(" 9Q5 ")
18
+ refute reader.geohash?(" 9aQ5 ")
19
+ assert reader.geohash?("u58seqshdz50")
20
+ end
21
+
22
+ def test_recognizes_geojson
23
+ assert reader.geojson?({type: "Point", coordinates: [0,1]}.to_json)
24
+ refute reader.geojson?("a")
25
+ end
26
+
27
+ def test_parse_objects
28
+ parsed = TestData.mixed_stream.to_a
29
+ assert parsed[0].is_a? Geoq::Geohash
30
+ assert parsed[1].is_a? Geoq::GeoJson
31
+ assert parsed[2].is_a? Geoq::Wkt
32
+ assert parsed[3].is_a? Geoq::GeoJson
33
+ assert parsed[4].is_a? Geoq::LatLon
34
+ end
35
+
36
+ def test_parsing_lat_lons
37
+ assert reader.latlon?("0,0")
38
+ assert reader.latlon?("0, 0")
39
+ assert reader.latlon?("123.456,98.2")
40
+ assert reader.latlon?(" -123.456,-98.2 ")
41
+ end
42
+
43
+ def test_correct_lat_lon_ordering
44
+ entities = TestData.stream(["1,2", "34, -118"]).to_a
45
+ assert_equal entities[0].entity.x, 2
46
+ assert_equal entities[0].entity.y, 1
47
+ assert_equal entities[1].entity.x, -118
48
+ assert_equal entities[1].entity.y, 34
49
+ end
50
+
51
+ def test_lat_lon_geohash_conversion
52
+ point = TestData.stream(["34, -118"]).first
53
+ assert_equal "9qh1", point.gh_string(4)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,28 @@
1
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
2
+ require "geoq"
3
+
4
+ require "minitest/autorun"
5
+ require "minitest/reporters"
6
+ require "minitest/spec"
7
+
8
+ Minitest::Reporters.use! Minitest::Reporters::DefaultReporter.new
9
+
10
+ module Geoq::TestData
11
+ def self.stream(strings)
12
+ r,w = IO.pipe
13
+ strings.each do |s|
14
+ w.puts(s)
15
+ end
16
+ w.close
17
+ Geoq::GeomReader.new(r)
18
+ end
19
+
20
+ def self.mixed_stream
21
+ inputs = ["9q5",
22
+ {type: "Point", coordinates: [0,1]}.to_json,
23
+ "POINT (1.0 2.0)",
24
+ {type: "Feature", properties: {a: "b"}, geometry: {type: "Point", coordinates: [3,4]}}.to_json,
25
+ "34.52,-118.3"]
26
+ stream(inputs)
27
+ end
28
+ end
@@ -0,0 +1,35 @@
1
+ require "test_helper"
2
+
3
+ module Geoq
4
+ class UsageTest < Minitest::Test
5
+ def script_setup
6
+ File.read("./usage.md").match(/```setup\n(.+)\n```/)[1]
7
+ end
8
+
9
+ def examples
10
+ File.read("./usage.md").scan(/```example\n(.*?)```\n/m).to_a.flatten.map { |match| match.split("\n=> ") }
11
+ end
12
+
13
+ def setup
14
+ end
15
+
16
+ def test_reads_examples
17
+ assert_equal 15, examples.count
18
+ end
19
+
20
+ def checked_command(command)
21
+ result = `#{command}`
22
+ if $? != 0
23
+ raise RuntimeError.new("Command #{command} exited with non-zero response code.")
24
+ end
25
+ result
26
+ end
27
+
28
+ def test_run_examples
29
+ examples.each do |command, expected_output|
30
+ result = checked_command(command)
31
+ assert_equal expected_output, result, "Checked example: #{command} failed"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,150 @@
1
+ # Geoq Usage
2
+
3
+ ```setup
4
+ alias geoqqq="bundle exec geoq" &&
5
+ ```
6
+
7
+ ## GeoJSON (`geoq gj`)
8
+
9
+ Output a geo feature as a GeoJSON Geometry
10
+
11
+ ```example
12
+ echo 9q5 | bundle exec bin/geoq gj geometry
13
+ => {"type":"Polygon","coordinates":[[[-119.53125,33.75],[-118.125,33.75],[-118.125,35.15625],[-119.53125,35.15625],[-119.53125,33.75]]]}
14
+ ```
15
+
16
+ Output a geo feature as a GeoJSON Feature
17
+
18
+ ```example
19
+ echo 9q5 | bundle exec bin/geoq gj feature
20
+ => {"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-119.53125,33.75],[-118.125,33.75],[-118.125,35.15625],[-119.53125,35.15625],[-119.53125,33.75]]]}}
21
+ ```
22
+
23
+ Combine 1 or more geo features into a GeoJSON Feature Collection
24
+
25
+ ```example
26
+ printf "9q5\n9q4\n" | bundle exec bin/geoq gj fc
27
+ => {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-119.53125,33.75],[-118.125,33.75],[-118.125,35.15625],[-119.53125,35.15625],[-119.53125,33.75]]]}},{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[-120.9375,33.75],[-119.53125,33.75],[-119.53125,35.15625],[-120.9375,35.15625],[-120.9375,33.75]]]}}]}
28
+ ```
29
+
30
+ Converts Geohashes, WKTs, Lat/Lons, and GeoJSON into GeoJSON
31
+
32
+ ```example
33
+ echo 9q5 | bundle exec bin/geoq gj geometry
34
+ => {"type":"Polygon","coordinates":[[[-119.53125,33.75],[-118.125,33.75],[-118.125,35.15625],[-119.53125,35.15625],[-119.53125,33.75]]]}
35
+ ```
36
+
37
+ ```example
38
+ echo "POINT (1.0 2.0)" | bundle exec bin/geoq gj geometry
39
+ => {"type":"Point","coordinates":[1.0,2.0]}
40
+ ```
41
+
42
+ ```example
43
+ echo "34.52,-118.3" | bundle exec bin/geoq gj geometry
44
+ => {"type":"Point","coordinates":[-118.3,34.52]}
45
+ ```
46
+
47
+ ```example
48
+ echo '{"type":"Point","coordinates":[-118.3,34.52]}' | bundle exec bin/geoq gj geometry
49
+ => {"type":"Point","coordinates":[-118.3,34.52]}
50
+ ```
51
+
52
+ ## Geohashes (`geoq gh`)
53
+
54
+ Convert a point to a geohash at specified level
55
+
56
+ ```example
57
+ echo "34,-118" | bundle exec bin/geoq gh point 4
58
+ => 9qh1
59
+ ```
60
+
61
+ Get children of a geohash
62
+
63
+ ```example
64
+ echo 9qh1 | bundle exec bin/geoq gh children
65
+ => 9qh10
66
+ 9qh11
67
+ 9qh12
68
+ 9qh13
69
+ 9qh14
70
+ 9qh15
71
+ 9qh16
72
+ 9qh17
73
+ 9qh18
74
+ 9qh19
75
+ 9qh1b
76
+ 9qh1c
77
+ 9qh1d
78
+ 9qh1e
79
+ 9qh1f
80
+ 9qh1g
81
+ 9qh1h
82
+ 9qh1j
83
+ 9qh1k
84
+ 9qh1m
85
+ 9qh1n
86
+ 9qh1p
87
+ 9qh1q
88
+ 9qh1r
89
+ 9qh1s
90
+ 9qh1t
91
+ 9qh1u
92
+ 9qh1v
93
+ 9qh1w
94
+ 9qh1x
95
+ 9qh1y
96
+ 9qh1z
97
+ ```
98
+
99
+ Get neighbors of a geohash
100
+
101
+ ```example
102
+ echo 9qh1 | bundle exec bin/geoq gh neighbors
103
+ => 9qh4
104
+ 9qh6
105
+ 9qh3
106
+ 9qh2
107
+ 9qh0
108
+ 9q5b
109
+ 9q5c
110
+ 9q5f
111
+ ```
112
+
113
+ Include the original geohash in the neighbors list:
114
+
115
+ ```example
116
+ echo 9qh1 | bundle exec bin/geoq gh neighbors -i
117
+ => 9qh1
118
+ 9qh4
119
+ 9qh6
120
+ 9qh3
121
+ 9qh2
122
+ 9qh0
123
+ 9q5b
124
+ 9q5c
125
+ 9q5f
126
+ ```
127
+
128
+ ## WKT (`geoq wkt`)
129
+
130
+ Converts Geohashes, WKTs, Lat/Lons, and GeoJSON into GeoJSON
131
+
132
+ ```example
133
+ echo 9q5 | bundle exec bin/geoq wkt
134
+ => POLYGON ((-119.53125 33.75, -118.125 33.75, -118.125 35.15625, -119.53125 35.15625, -119.53125 33.75))
135
+ ```
136
+
137
+ ```example
138
+ echo "POINT (1.0 2.0)" | bundle exec bin/geoq wkt
139
+ => POINT (1.0 2.0)
140
+ ```
141
+
142
+ ```example
143
+ echo "34.52,-118.3" | bundle exec bin/geoq wkt
144
+ => POINT (-118.3 34.52)
145
+ ```
146
+
147
+ ```example
148
+ echo '{"type":"Point","coordinates":[-118.3,34.52]}' | bundle exec bin/geoq wkt
149
+ => POINT (-118.3 34.52)
150
+ ```
metadata ADDED
@@ -0,0 +1,193 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: geoq
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.pre
5
+ platform: ruby
6
+ authors:
7
+ - Horace Williams
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-05-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '12.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '12.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aruba
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.14'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.14'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.11'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.11'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-reporters
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.2'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.11'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.11'
83
+ - !ruby/object:Gem::Dependency
84
+ name: gli
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.17'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.17'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rgeo
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rgeo-geojson
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '2.0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pr_geohash
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.0'
139
+ description:
140
+ email: horace@worace.works
141
+ executables:
142
+ - geoq
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".travis.yml"
148
+ - Gemfile
149
+ - Gemfile.lock
150
+ - README.md
151
+ - README.rdoc
152
+ - Rakefile
153
+ - bin/geoq
154
+ - geoq.gemspec
155
+ - lib/geoq.rb
156
+ - lib/geoq/commands/base.rb
157
+ - lib/geoq/commands/geohash.rb
158
+ - lib/geoq/commands/geojson.rb
159
+ - lib/geoq/entity.rb
160
+ - lib/geoq/errors.rb
161
+ - lib/geoq/geom_reader.rb
162
+ - lib/geoq/version.rb
163
+ - test/commands_test.rb
164
+ - test/geom_reader_test.rb
165
+ - test/test_helper.rb
166
+ - test/usage_test.rb
167
+ - usage.md
168
+ homepage: https://github.com/worace/geoq
169
+ licenses:
170
+ - MIT
171
+ metadata: {}
172
+ post_install_message:
173
+ rdoc_options: []
174
+ require_paths:
175
+ - lib
176
+ - lib
177
+ required_ruby_version: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ required_rubygems_version: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - ">"
185
+ - !ruby/object:Gem::Version
186
+ version: 1.3.1
187
+ requirements: []
188
+ rubyforge_project:
189
+ rubygems_version: 2.6.14.1
190
+ signing_key:
191
+ specification_version: 4
192
+ summary: geoq is a command-line utility for GIS format conversion and common GIS operations
193
+ test_files: []