geo_monitor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []