geo_monitor 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9dfd57782846f5f060a3a1e2c036686653d71551
4
+ data.tar.gz: 6a1ed0c2c01bd0cc36213f0d5676f5bb098796b4
5
+ SHA512:
6
+ metadata.gz: fee0dd08dedd152e716b4a6704841e57ee2babe4199e91f539f7ce358ac7add5d5941983a1dc6852ed5d1079abef2f0bca2dfae644e540c5d0b3c8f78ffad901
7
+ data.tar.gz: 604eb560be8f049ca89abdaa18b41f93182d8e6fdb2be9ddc6bb971e2723e2c2b03eabae1579178cbbb330f8b51552afccb894d81d10f5bf50cd8c9e14409f6c
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2017 The Board of Trustees of the Leland Stanford Junior University
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # GeoMonitor
2
+ Short description and motivation.
3
+
4
+ ## Usage
5
+ How to use my plugin.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'geo_monitor'
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ```bash
21
+ $ gem install geo_monitor
22
+ ```
23
+
24
+ ## Contributing
25
+ Contribution directions go here.
26
+
27
+ ## License
28
+ The gem is available as open source under the terms of the [Apache License 2.0](https://opensource.org/licenses/Apache-2.0).
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'bundler/gem_tasks'
8
+ require 'engine_cart/rake_task'
9
+
10
+ EngineCart.fingerprint_proc = EngineCart.rails_fingerprint_proc
11
+
12
+ namespace :geomonitor do
13
+ desc 'Create the test rails app'
14
+ task generate: ['engine_cart:generate'] do
15
+ end
16
+ end
17
+
18
+ task ci: ['geomonitor:generate'] do
19
+ Rake::Task['spec'].invoke
20
+ end
21
+
22
+ begin
23
+ require 'rspec/core/rake_task'
24
+
25
+ RSpec::Core::RakeTask.new(:spec)
26
+
27
+ task default: :ci
28
+ rescue LoadError
29
+ # no rspec available
30
+ end
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/geo_monitor .js
2
+ //= link_directory ../stylesheets/geo_monitor .css
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,5 @@
1
+ module GeoMonitor
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module GeoMonitor
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module GeoMonitor
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module GeoMonitor
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module GeoMonitor
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module GeoMonitor
2
+ module GeoMonitor
3
+ def self.table_name_prefix
4
+ 'geo_monitor_geo_monitor_'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,38 @@
1
+ module GeoMonitor
2
+ class Layer < ApplicationRecord
3
+ has_many :statuses
4
+
5
+ ##
6
+ # @param [String] schema_json
7
+ def self.from_geoblacklight(schema_json)
8
+ schema = JSON.parse(schema_json)
9
+ references = JSON.parse(schema['dct_references_s'])
10
+ find_or_create_by(slug: schema['layer_slug_s']) do |layer|
11
+ layer.checktype = 'WMS'
12
+ layer.layername = schema['layer_id_s']
13
+ layer.bbox = schema['solr_geom']
14
+ layer.url = references['http://www.opengis.net/def/serviceType/ogc/wms']
15
+ layer.active = true
16
+ end
17
+ end
18
+
19
+ def bounding_box
20
+ w, e, n, s = bbox.delete('ENVELOPE(').delete(')').delete(' ').split(',')
21
+ GeoMonitor::BoundingBox.new(north: n, south: s, east: e, west: w)
22
+ end
23
+
24
+ def check
25
+ response = nil
26
+ time = Benchmark.measure do
27
+ response = GeoMonitor::Requests::WMS.new(
28
+ bbox: bounding_box, url: url, layers: layername
29
+ ).tile
30
+ end
31
+ GeoMonitor::Status.from_response(response, self, time.real.to_f)
32
+ end
33
+
34
+ def availability_score
35
+ statuses.where(res_code: '200').count.to_f / statuses.count
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ module GeoMonitor
2
+ class Status < ApplicationRecord
3
+ belongs_to :layer, counter_cache: true, touch: true
4
+ before_create :limit_by_layer
5
+
6
+ ##
7
+ # Limits the number of statuses per layer to prevent a ballooing database
8
+ def limit_by_layer
9
+ statuses_by_layer = self.class.where(layer: layer).count
10
+ max = GeoMonitor::Engine.config.max_status_per_layer
11
+ self.class
12
+ .where(layer: layer)
13
+ .last(statuses_by_layer - max + 1)
14
+ .map(&:destroy) if statuses_by_layer >= max
15
+ end
16
+
17
+ ##
18
+ # @param [Faraday::Resposne] response
19
+ # @param [GeoMonitor::Layer] layer
20
+ # @param [Float] time
21
+ def self.from_response(response, layer, time)
22
+ create(
23
+ res_time: time.to_f,
24
+ res_code: response.status,
25
+ submitted_query: response.env[:url].to_s,
26
+ layer: layer,
27
+ res_headers: response.headers
28
+ )
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Geo monitor</title>
5
+ <%= stylesheet_link_tag "geo_monitor/application", media: "all" %>
6
+ <%= javascript_include_tag "geo_monitor/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ GeoMonitor::Engine.routes.draw do
2
+ end
@@ -0,0 +1,15 @@
1
+ class CreateGeoMonitorLayers < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :geo_monitor_layers do |t|
4
+ t.string :slug, index: true, unique: true
5
+ t.string :checktype, index: true
6
+ t.string :layername
7
+ t.string :access, index: true
8
+ t.string :bbox
9
+ t.string :url
10
+ t.integer :statuses_count
11
+ t.boolean :active, index: true
12
+ t.timestamps
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ class CreateGeoMonitorStatuses < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :geo_monitor_statuses do |t|
4
+ t.string :res_code, index: true
5
+ t.string :res_headers
6
+ t.decimal :res_time
7
+ t.text :submitted_query
8
+ t.references :layer, index: true
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module GeoMonitor
2
+ ##
3
+ # GeoMonitor Installer
4
+ class Install < Rails::Generators::Base
5
+ source_root File.expand_path('../templates', __FILE__)
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ require "geo_monitor/engine"
2
+
3
+ module GeoMonitor
4
+ # A simple structure to conform to Faraday::Response
5
+ FailedResponse = Struct.new(:env, :status, :headers)
6
+ end
@@ -0,0 +1,65 @@
1
+ module GeoMonitor
2
+ ##
3
+ # Bounding Box
4
+ class BoundingBox
5
+ attr_reader :north, :south, :east, :west
6
+
7
+ def initialize(north:, south:, east:, west:)
8
+ @north = north.to_f
9
+ @south = south.to_f
10
+ @east = east.to_f
11
+ @west = west.to_f
12
+ end
13
+
14
+ def to_s
15
+ "#{west},#{south},#{east},#{north}"
16
+ end
17
+
18
+ ##
19
+ # Calculates the tilebounds for a tile within the existing bounds.
20
+ def tile_bounds
21
+ tile = tile_number
22
+ zoom = zoom_level
23
+ sw = GeoMonitor::LatLngPoint.from_number(tile[:x], tile[:y], zoom).to_3857
24
+ ne = GeoMonitor::LatLngPoint.from_number(tile[:x] + 1, tile[:y] - 1, zoom).to_3857
25
+ self.class.new(
26
+ north: ne.lat,
27
+ east: ne.lng,
28
+ south: sw.lat,
29
+ west: sw.lng
30
+ )
31
+ end
32
+
33
+ ##
34
+ # Calculates the "zoom level" that can best view the bounds.
35
+ # http://wiki.openstreetmap.org/wiki/Zoom_levels
36
+ def zoom_level
37
+ lat_diff = north - south
38
+ lng_diff = east - west
39
+ max_diff = [lat_diff, lng_diff].max
40
+
41
+ if max_diff < GeoMonitor::DEGREES_IN_CIRCLE / 2**20
42
+ zoom = 21
43
+ else
44
+ zoom = -1 * ((Math.log(max_diff) / Math.log(2)) - (Math.log(GeoMonitor::DEGREES_IN_CIRCLE) / Math.log(2)))
45
+ zoom = 1 if zoom < 1
46
+ end
47
+ zoom.ceil
48
+ end
49
+
50
+ ##
51
+ # See: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Lon..2Flat._to_tile_numbers_2
52
+ def tile_number
53
+ lat_rad = south / 180 * Math::PI
54
+ n = 2.0**zoom_level
55
+ x = ((west + 180.0) / GeoMonitor::DEGREES_IN_CIRCLE * n).to_i
56
+ y = ((1.0 - Math.log(Math.tan(lat_rad) + (1 / Math.cos(lat_rad))) / Math::PI) / 2.0 * n)
57
+ y = if y.infinite?.nil?
58
+ y.to_i
59
+ else
60
+ x
61
+ end
62
+ { x: x, y: y }
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,18 @@
1
+ require 'rails/all'
2
+ require 'faraday'
3
+ require 'geo_monitor/bounding_box'
4
+ require 'geo_monitor/lat_lng_point'
5
+ require 'geo_monitor/requests/wms'
6
+
7
+ module GeoMonitor
8
+ R = 6_378_137 # Radius of Earth in meters
9
+ DEGREES_IN_CIRCLE = 360.0
10
+
11
+ ##
12
+ # Top level Rails Engine class
13
+ class Engine < ::Rails::Engine
14
+ isolate_namespace GeoMonitor
15
+
16
+ GeoMonitor::Engine.config.max_status_per_layer = 5
17
+ end
18
+ end
@@ -0,0 +1,32 @@
1
+ module GeoMonitor
2
+ class LatLngPoint
3
+ attr_accessor :lat, :lng
4
+ def initialize(lat: 0, lng: 0)
5
+ @lat = lat.to_f
6
+ @lng = lng.to_f
7
+ end
8
+
9
+ ##
10
+ # This needs better documentation, but projecting from EPSG:4326 to
11
+ # EPSG:3857
12
+ def to_3857
13
+ d = Math::PI / 180
14
+ max = 1 - 1E-15
15
+ sin = [[Math.sin(lng * d), max].min, -max].max
16
+ self.class.new(
17
+ lat: GeoMonitor::R * lat * d,
18
+ lng: GeoMonitor::R * Math.log((1 + sin) / (1 - sin)) / 2
19
+ )
20
+ end
21
+
22
+ # Get the lat/lng for a specific tile at a zoom level
23
+ # From http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Pseudo-code
24
+ def self.from_number(xtile, ytile, zoom)
25
+ n = 2.0**zoom
26
+ lng = xtile / n * 360.0 - 180.0
27
+ lat_rad = Math.atan(Math.sinh(Math::PI * (1 - 2 * ytile / n)))
28
+ lat = 180.0 * (lat_rad / Math::PI)
29
+ new(lat: lat, lng: lng)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,58 @@
1
+ module GeoMonitor
2
+ module Requests
3
+ ##
4
+ # Crafts a WMS request
5
+ class WMS
6
+ attr_reader :bbox, :url, :layers
7
+
8
+ def initialize(bbox:, url:, layers:)
9
+ @bbox = bbox
10
+ @url = url
11
+ @layers = layers
12
+ end
13
+
14
+ ##
15
+ # Parameters used for the WMS request.
16
+ def request_params
17
+ {
18
+ 'SERVICE' => 'WMS',
19
+ 'VERSION' => '1.1.1',
20
+ 'REQUEST' => 'GetMap',
21
+ 'LAYERS' => layers,
22
+ 'STYLES' => '',
23
+ 'CRS' => 'EPSG:900913',
24
+ 'SRS' => 'EPSG:3857',
25
+ 'BBOX' => bbox.tile_bounds.to_s,
26
+ 'WIDTH' => '256',
27
+ 'HEIGHT' => '256',
28
+ 'FORMAT' => 'image/png',
29
+ 'TILED' => true
30
+ }
31
+ end
32
+
33
+ ##
34
+ # Request the tile.
35
+ def tile
36
+ unless url.present?
37
+ return GeoMonitor::FailedResponse.new(
38
+ { url: url }, 'No URL provided', {}
39
+ )
40
+ end
41
+ conn = Faraday.new(url: url)
42
+ begin
43
+ conn.get do |request|
44
+ request.params = request_params
45
+ request.options.timeout = 10
46
+ request.options.open_timeout = 10
47
+ end
48
+ rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
49
+ GeoMonitor::FailedResponse.new(
50
+ { url: conn.url_prefix.to_s },
51
+ e.class,
52
+ nil
53
+ )
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ module GeoMonitor
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :geo_monitor do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: geo_monitor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jack Reed
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-12-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.1.4
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 5.1.4
27
+ - !ruby/object:Gem::Dependency
28
+ name: faraday
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: engine_cart
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: A Rails engine for monitoring geo webservices
98
+ email:
99
+ - phillipjreed@gmail.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - LICENSE
105
+ - README.md
106
+ - Rakefile
107
+ - app/assets/config/geo_monitor_manifest.js
108
+ - app/assets/javascripts/geo_monitor/application.js
109
+ - app/assets/stylesheets/geo_monitor/application.css
110
+ - app/controllers/geo_monitor/application_controller.rb
111
+ - app/helpers/geo_monitor/application_helper.rb
112
+ - app/jobs/geo_monitor/application_job.rb
113
+ - app/mailers/geo_monitor/application_mailer.rb
114
+ - app/models/geo_monitor/application_record.rb
115
+ - app/models/geo_monitor/geo_monitor.rb
116
+ - app/models/geo_monitor/layer.rb
117
+ - app/models/geo_monitor/status.rb
118
+ - app/views/layouts/geo_monitor/application.html.erb
119
+ - config/routes.rb
120
+ - db/migrate/20171212180244_create_geo_monitor_layers.rb
121
+ - db/migrate/20171212181423_create_geo_monitor_statuses.rb
122
+ - lib/generators/geo_monitor/install_generator.rb
123
+ - lib/geo_monitor.rb
124
+ - lib/geo_monitor/bounding_box.rb
125
+ - lib/geo_monitor/engine.rb
126
+ - lib/geo_monitor/lat_lng_point.rb
127
+ - lib/geo_monitor/requests/wms.rb
128
+ - lib/geo_monitor/version.rb
129
+ - lib/tasks/geo_monitor_tasks.rake
130
+ homepage: https://github.com/geoblacklight/geo_monitor
131
+ licenses:
132
+ - Apache 2.0
133
+ metadata: {}
134
+ post_install_message:
135
+ rdoc_options: []
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ requirements: []
149
+ rubyforge_project:
150
+ rubygems_version: 2.6.14
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: A Rails engine for monitoring geo webservices
154
+ test_files: []