sinatra_wms 0.0.1 → 0.0.2
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.
- data/Gemfile +3 -0
- data/README.rdoc +81 -0
- data/Rakefile +6 -0
- data/lib/sinatra_wms.rb +12 -4
- data/lib/sinatra_wms/rmagick_extension.rb +19 -1
- data/lib/sinatra_wms/sinatra_extension.rb +22 -2
- data/lib/sinatra_wms/version.rb +1 -1
- data/sinatra_wms.gemspec +1 -0
- metadata +6 -6
- data/README.md +0 -89
data/Gemfile
CHANGED
data/README.rdoc
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
= SintraWMS
|
2
|
+
|
3
|
+
== What does SinatraWMS do?
|
4
|
+
|
5
|
+
Suppose you have a huge amount of data containing geolocations which you want to show graphically.
|
6
|
+
|
7
|
+
One way to achieve this would be to create a more or less huge image file with RMagick or something and just draw the data into this file.
|
8
|
+
This approach has two drawbacks: Either the image file you create will get really huge - or it won't get that large but doesn't provide the needed or wanted resolution. Also, you will just get a black image with white dots on it or something - not very nice. A real map as background would be nice.
|
9
|
+
|
10
|
+
Another way would be to use a Google Map or something and use a JavaScript to add lots and lots of markers to the map.
|
11
|
+
You could zoom in and out an such - but you'd have to care about getting your data to JavaScript to add the markers. And it won't be that fast once you reach some kind of limit.
|
12
|
+
|
13
|
+
The solution for this problem: WMS (Web Map Service).
|
14
|
+
This gem helps you to provide a WMS to be used with OpenLayers. OpenLayers will display the OpenStreetMap at a configurable Opacity and add your data on top of it. Whenever you scroll or zoom around the map, OpenLayers will request images of a part of the visible screen. Your code has to generate them on-the-fly and deliver them back.
|
15
|
+
|
16
|
+
|
17
|
+
== How do I use SinatraWMS?
|
18
|
+
|
19
|
+
Let's build a sample app.
|
20
|
+
In my example, I will use SinatraWMS to display the locations of my geolocated tweets.
|
21
|
+
|
22
|
+
+Gemfile+:
|
23
|
+
source :rubygems
|
24
|
+
|
25
|
+
gem "sinatra"
|
26
|
+
gem "rmagick"
|
27
|
+
gem "sinatra_wms"
|
28
|
+
gem "activerecord" # I'm using ActiveRecord to retrieve my data from the Database. You can do this your own way.
|
29
|
+
|
30
|
+
+sinatra_wms_test.rb+:
|
31
|
+
# This is just stuff for my ActiveRecord-addiction. ;-)
|
32
|
+
# You can use DataMapper, mysql, whatever.
|
33
|
+
|
34
|
+
ActiveRecord::Base.establish_connection(
|
35
|
+
:adapter => 'mysql',
|
36
|
+
:host => 'localhost',
|
37
|
+
:username => 'twirror',
|
38
|
+
:password => 'secret',
|
39
|
+
:database => 'twirror',
|
40
|
+
:encoding => 'UTF8')
|
41
|
+
|
42
|
+
class Tweet < ActiveRecord::Base
|
43
|
+
end
|
44
|
+
|
45
|
+
before do
|
46
|
+
ActiveRecord::Base.connection.verify!
|
47
|
+
end
|
48
|
+
# End of my ActiveRecord-stuff.
|
49
|
+
|
50
|
+
# This is the main entry point for this app.
|
51
|
+
# This code just outputs generic HTML code to load OpenLayers, show the OSM
|
52
|
+
# as transparent background and our WMS stuff on top of that.
|
53
|
+
get '/' do
|
54
|
+
SinatraWMS::get_html_for_map_at url("/wms"), :opacity=>0.3, :title=>"My Tweets"
|
55
|
+
end
|
56
|
+
|
57
|
+
# This code gets run for every image requested by OpenLayers. It is given a
|
58
|
+
# RMagick::Draw object on which you can immediately start painting as well as
|
59
|
+
# a hash containing some information about this request.
|
60
|
+
wms '/wms' do |canvas, options|
|
61
|
+
# Select matching Tweets from the DB.
|
62
|
+
# :bbox denotes the area visible in the current image. So we only select
|
63
|
+
# Tweets with coordinates within this box to draw.
|
64
|
+
# We also use a limit here, because Drawing lots and lots of points with
|
65
|
+
# RMagick is pretty slow. Since zooming in reduces the area each image has
|
66
|
+
# to span, more and more points are going to show up when we zoom in.
|
67
|
+
rel = Tweet.where(:sender_name=>'fabianonline').
|
68
|
+
where("geo_lat IS NOT NULL and geo_long IS NOT NULL").
|
69
|
+
where("geo_lat>=? and geo_lat<=?", options[:bbox][:wgs84][0][0], options[:bbox][:wgs84][1][0]).
|
70
|
+
where("geo_long>=? and geo_long<=?", options[:bbox][:wgs84][0][1], options[:bbox][:wgs84][1][1]).
|
71
|
+
limit(1000)
|
72
|
+
|
73
|
+
rel.each do |tweet|
|
74
|
+
# Draw each point. This gem extends RMagick::Draw to provide some
|
75
|
+
# methods which directly take coordinates in WGS84 (that's the most)
|
76
|
+
# commonly used format for coordinates. You know, longitude between
|
77
|
+
# -180 (West) and 180 (East) degrees and latitude between -90 (South)
|
78
|
+
# and 90 (North) degrees).
|
79
|
+
canvas.point_wgs84(tweet.geo_long, tweet.geo_lat)
|
80
|
+
end
|
81
|
+
end
|
data/Rakefile
CHANGED
data/lib/sinatra_wms.rb
CHANGED
@@ -3,16 +3,24 @@ require "sinatra_wms/sinatra_extension"
|
|
3
3
|
require "sinatra_wms/rmagick_extension"
|
4
4
|
|
5
5
|
module SinatraWMS
|
6
|
+
|
7
|
+
##
|
8
|
+
# Convert a set of coordinates from sperical mercator to WGS84.
|
6
9
|
def self.merc_to_latlon(x, y)
|
7
10
|
lon = (x / 6378137.0) / Math::PI * 180
|
8
11
|
lat = Math::atan(Math::sinh(y / 6378137.0)) / Math::PI * 180
|
9
12
|
return [lat, lon]
|
10
13
|
end
|
11
|
-
|
12
|
-
def self.deg_sin(x)
|
13
|
-
Math.sin(x * Math::PI / 180)
|
14
|
-
end
|
15
14
|
|
15
|
+
##
|
16
|
+
# Returns generic HTML code to display a transparent OSM map and images from the WMS.
|
17
|
+
#
|
18
|
+
# * +url+ has to be the full URL to the WMS. Since this module can't use Sinatra's helper,
|
19
|
+
# please call it using this helper, e.g. +url("/wms")+.
|
20
|
+
# * Other options used are:
|
21
|
+
# * +:title+ - Sets the HTML title attribute.
|
22
|
+
# * +:opacity:+ - Sets the opacity of the OSM map in the background.
|
23
|
+
# * +:datasource_name+ - Sets the name of the WMS source in the layer selector menu.
|
16
24
|
def self.get_html_for_map_at(url, options={})
|
17
25
|
options[:title] ||= "Sinatra-WMS"
|
18
26
|
options[:opacity] ||= 1
|
@@ -1,10 +1,18 @@
|
|
1
1
|
require 'RMagick'
|
2
2
|
|
3
|
+
##
|
4
|
+
# Monkeypatching RMagick to add some methods to +Magick::Draw+.
|
3
5
|
module Magick
|
4
6
|
class Draw
|
7
|
+
##
|
8
|
+
# Which methods with one coordinates as parameter to add +_wgs84+ method to
|
5
9
|
WMS_FUNCTIONS_SINGLE = %w(color matte point text bigpoint)
|
10
|
+
##
|
11
|
+
# Which methods with two coordinates as parameter to add +_wgs84+ method to
|
6
12
|
WMS_FUNCTIONS_DOUBLE = %w(circle ellipse line rectangle roundrectangle)
|
7
13
|
|
14
|
+
##
|
15
|
+
# We use method_missing to catch calls to the +_wgs84+ methods.
|
8
16
|
def method_missing(sym, *args, &block)
|
9
17
|
result = sym.to_s.match /^([a-z0-9_]+)_(?:wgs84)$/
|
10
18
|
if result && respond_to?(result[1]) && @wms_settings
|
@@ -20,6 +28,9 @@ module Magick
|
|
20
28
|
end
|
21
29
|
end
|
22
30
|
|
31
|
+
##
|
32
|
+
# This method gets the data from +SinatraExtension+ and does some calculations
|
33
|
+
# to have values prepared when they're needed.
|
23
34
|
def wms_settings=(hash)
|
24
35
|
hash[:min_sin_y] = Math::asinh(Math::tan(hash[:bbox][0][0] / 180.0 * Math::PI)) * 6378137.0
|
25
36
|
hash[:max_sin_y] = Math::asinh(Math::tan(hash[:bbox][1][0] / 180.0 * Math::PI)) * 6378137.0
|
@@ -27,17 +38,24 @@ module Magick
|
|
27
38
|
hash[:factor_x] = hash[:width] / (hash[:bbox][1][1] - hash[:bbox][0][1])
|
28
39
|
@wms_settings = hash
|
29
40
|
end
|
30
|
-
|
41
|
+
|
42
|
+
##
|
43
|
+
# The same as +point+, but draws a "big point" with 3 pixels width and height.
|
31
44
|
def bigpoint(x, y)
|
32
45
|
rectangle(x-1, y-1, x+1, y+1)
|
33
46
|
end
|
34
47
|
|
48
|
+
##
|
49
|
+
# See +method_missing+
|
35
50
|
def respond_to?(sym)
|
36
51
|
result = sym.to_s.match /^([a-z0-9_]+)_(?:wgs84)$/
|
37
52
|
(result && super(result[1]) && @wms_settings) || super(sym)
|
38
53
|
end
|
39
54
|
|
40
55
|
private
|
56
|
+
##
|
57
|
+
# Converts WGS84 coordinates to pixel values.
|
58
|
+
# Gets called via +method_missing+, whenever one of the +*_wgs84* methods is used.
|
41
59
|
def latlon_to_pixels(x, y)
|
42
60
|
raise "wms_settings is missing values" unless [:bbox, :factor_x, :min_sin_y, :max_sin_y, :height].all?{|v| @wms_settings.has_key?(v)}
|
43
61
|
x = ((x - @wms_settings[:bbox][0][1]) * @wms_settings[:factor_x]).round
|
@@ -1,6 +1,19 @@
|
|
1
1
|
require 'sinatra/base'
|
2
|
+
##
|
3
|
+
# +Sinatra+ is extended to contain the method +wms+, which controls all the
|
4
|
+
# WMS-relevant stuff.
|
2
5
|
module Sinatra
|
3
6
|
module WMS
|
7
|
+
##
|
8
|
+
# Reacts on WMS calls
|
9
|
+
#
|
10
|
+
# Defines an +url+ to react on WMS calls.
|
11
|
+
# If we get a matching request, we will do some fancy calculations to the
|
12
|
+
# parameters given (as in "convert the bounding box from sperical mercator
|
13
|
+
# to the well-known WGS84 projection" and so on.)
|
14
|
+
# Also, we will prepare an image and a matching canvas which we give to the
|
15
|
+
# given block.
|
16
|
+
# After the block has run, we return the image to the requestee.
|
4
17
|
def wms(url, &block)
|
5
18
|
get url do
|
6
19
|
headers "Content-Type" => "image/png"
|
@@ -10,7 +23,7 @@ module Sinatra
|
|
10
23
|
# interpret :width and :height as integer
|
11
24
|
[:width, :height].each {|what| options[what] = options[what].to_i}
|
12
25
|
|
13
|
-
# convert bounding box coordinates
|
26
|
+
# convert bounding box coordinates to WGS84
|
14
27
|
bbox = options[:bbox].split(',').collect{|v| v.to_f}
|
15
28
|
options[:bbox] = {:original => [[bbox[0], bbox[1]], [bbox[2], bbox[3]]]}
|
16
29
|
if options[:srs]=="EPSG:900913"
|
@@ -19,19 +32,26 @@ module Sinatra
|
|
19
32
|
else
|
20
33
|
raise "Unexpected Projection (srs): #{options[:srs]}"
|
21
34
|
end
|
35
|
+
|
36
|
+
# calculate the current zoom level (between 0 and 17, with 0 being the while world)
|
22
37
|
options[:zoom] = (17-((Math.log((options[:bbox][:google][1][0]-options[:bbox][:google][0][0]).abs*3.281/500) / Math.log(2)).round))
|
23
|
-
|
38
|
+
|
39
|
+
# generate a transparent image, an empty canvas and set the default
|
40
|
+
# drawing color to black
|
24
41
|
image = Magick::Image.new(options[:width], options[:height]) { self.background_color = 'transparent' }
|
25
42
|
gc = Magick::Draw.new
|
26
43
|
gc.stroke("black")
|
27
44
|
|
45
|
+
# The canvas needs some values for the calculations for the +*_wgs84+ methods.
|
28
46
|
gc.wms_settings = {
|
29
47
|
:bbox => options[:bbox][:wgs84],
|
30
48
|
:width => options[:width],
|
31
49
|
:height => options[:height]}
|
32
50
|
|
51
|
+
# Call the block
|
33
52
|
block.call(gc, options)
|
34
53
|
|
54
|
+
# Add the canvas to the image and return the resulting image as PNG.
|
35
55
|
gc.draw(image) rescue nil
|
36
56
|
image.to_blob {self.format="png"}
|
37
57
|
end
|
data/lib/sinatra_wms/version.rb
CHANGED
data/sinatra_wms.gemspec
CHANGED
@@ -20,5 +20,6 @@ This gem allows you to very easily represent your data via a WMS. On one hand it
|
|
20
20
|
s.files = `git ls-files`.split("\n")
|
21
21
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
22
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.extra_rdoc_files = ["README.rdoc"]
|
23
24
|
s.require_paths = ["lib"]
|
24
25
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sinatra_wms
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Fabian Schlenz (@fabianonline)
|
@@ -57,12 +57,12 @@ executables: []
|
|
57
57
|
|
58
58
|
extensions: []
|
59
59
|
|
60
|
-
extra_rdoc_files:
|
61
|
-
|
60
|
+
extra_rdoc_files:
|
61
|
+
- README.rdoc
|
62
62
|
files:
|
63
63
|
- .gitignore
|
64
64
|
- Gemfile
|
65
|
-
- README.
|
65
|
+
- README.rdoc
|
66
66
|
- Rakefile
|
67
67
|
- lib/sinatra_wms.rb
|
68
68
|
- lib/sinatra_wms/rmagick_extension.rb
|
data/README.md
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
SintraWMS
|
2
|
-
=========
|
3
|
-
|
4
|
-
What does SinatraWMS do?
|
5
|
-
------------------------
|
6
|
-
|
7
|
-
Suppose you have a huge amount of data containing geolocations which you want to show graphically.
|
8
|
-
|
9
|
-
One way to achieve this would be to create a more or less huge image file with RMagick or something and just draw the data into this file.
|
10
|
-
This approach has two drawbacks: Either the image file you create will get really huge - or it won't get that large but doesn't provide the needed or wanted resolution. Also, you will just get a black image with white dots on it or something - not very nice. A real map as background would be nice.
|
11
|
-
|
12
|
-
Another way would be to use a Google Map or something and use a JavaScript to add lots and lots of markers to the map.
|
13
|
-
You could zoom in and out an such - but you'd have to care about getting your data to JavaScript to add the markers. And it won't be that fast once you reach some kind of limit.
|
14
|
-
|
15
|
-
The solution for this problem: WMS (Web Map Service).
|
16
|
-
This gem helps you to provide a WMS to be used with OpenLayers. OpenLayers will display the OpenStreetMap at a configurable Opacity and add your data on top of it. Whenever you scroll or zoom around the map, OpenLayers will request images of a part of the visible screen. Your code has to generate them on-the-fly and deliver them back.
|
17
|
-
|
18
|
-
|
19
|
-
How do I use SinatraWMS?
|
20
|
-
------------------------
|
21
|
-
|
22
|
-
Let's build a sample app.
|
23
|
-
In my example, I will use SinatraWMS to display the locations of my geolocated tweets.
|
24
|
-
|
25
|
-
**Gemfile:**
|
26
|
-
```ruby
|
27
|
-
source :rubygems
|
28
|
-
|
29
|
-
gem "sinatra"
|
30
|
-
gem "rmagick"
|
31
|
-
gem "sinatra_wms"
|
32
|
-
gem "activerecord" # I'm using ActiveRecord to retrieve my data from the Database. You can do this your own way.
|
33
|
-
```
|
34
|
-
|
35
|
-
**sinatra_wms_test:**
|
36
|
-
```ruby
|
37
|
-
# This is just stuff for my ActiveRecord-addiction. ;-)
|
38
|
-
# You can use DataMapper, mysql, whatever.
|
39
|
-
|
40
|
-
ActiveRecord::Base.establish_connection(
|
41
|
-
:adapter => 'mysql',
|
42
|
-
:host => 'localhost',
|
43
|
-
:username => 'twirror',
|
44
|
-
:password => 'secret',
|
45
|
-
:database => 'twirror',
|
46
|
-
:encoding => 'UTF8')
|
47
|
-
|
48
|
-
class Tweet < ActiveRecord::Base
|
49
|
-
end
|
50
|
-
|
51
|
-
before do
|
52
|
-
ActiveRecord::Base.connection.verify!
|
53
|
-
end
|
54
|
-
# End of my ActiveRecord-stuff.
|
55
|
-
|
56
|
-
# This is the main entry point for this app.
|
57
|
-
# This code just outputs generic HTML code to load OpenLayers, show the OSM
|
58
|
-
# as transparent background and our WMS stuff on top of that.
|
59
|
-
get '/' do
|
60
|
-
SinatraWMS::get_html_for_map_at url("/wms"), :opacity=>0.3, :title=>"My Tweets"
|
61
|
-
end
|
62
|
-
|
63
|
-
# This code gets run for every image requested by OpenLayers. It is given a
|
64
|
-
# RMagick::Draw object on which you can immediately start painting as well as
|
65
|
-
# a hash containing some information about this request.
|
66
|
-
wms '/wms' do |canvas, options|
|
67
|
-
# Select matching Tweets from the DB.
|
68
|
-
# :bbox denotes the area visible in the current image. So we only select
|
69
|
-
# Tweets with coordinates within this box to draw.
|
70
|
-
# We also use a limit here, because Drawing lots and lots of points with
|
71
|
-
# RMagick is pretty slow. Since zooming in reduces the area each image has
|
72
|
-
# to span, more and more points are going to show up when we zoom in.
|
73
|
-
rel = Tweet.where(:sender_name=>'fabianonline').
|
74
|
-
where("geo_lat IS NOT NULL and geo_long IS NOT NULL").
|
75
|
-
where("geo_lat>=? and geo_lat<=?", options[:bbox][:wgs84][0][0], options[:bbox][:wgs84][1][0]).
|
76
|
-
where("geo_long>=? and geo_long<=?", options[:bbox][:wgs84][0][1], options[:bbox][:wgs84][1][1]).
|
77
|
-
limit(1000)
|
78
|
-
|
79
|
-
rel.each do |tweet|
|
80
|
-
# Draw each point. This gem extends RMagick::Draw to provide some
|
81
|
-
# methods which directly take coordinates in WGS84 (that's the most)
|
82
|
-
# commonly used format for coordinates. You know, longitude between
|
83
|
-
# -180 (West) and 180 (East) degrees and latitude between -90 (South)
|
84
|
-
# and 90 (North) degrees).
|
85
|
-
canvas.point_wgs84(tweet.geo_long, tweet.geo_lat)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
```
|