mapkit 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 inovex GmbH
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,55 @@
1
+ ## MapKit & TileKit
2
+
3
+ MapKit and TileKit are tools to help you develop a tile rendering service in
4
+ ruby. If you need to draw many markers in google maps then the performance
5
+ limit is reached fast. Google has advices to use up to 20 markers at a time.
6
+ With some tricks you can inrease the number of markes but with the cost of
7
+ inprintability.
8
+
9
+ If you want to take the full power of google maps you might have to render
10
+ your own tile over the tiles of google. Like layers where your layer is on top
11
+ of googles.
12
+
13
+ The system is very simple. You have to add a new Layer to your Google Maps that
14
+ request a tile with X, Y and Z like this:
15
+
16
+ var layer = new GTileLayer(null, 0, 21, {
17
+ isPng: true,
18
+ opacity: 1
19
+ });
20
+ layer.getTileUrl = function(tile, zoom) {
21
+ return "" + zoom + "/" + tile.x + "/" + tile.y + ".png";
22
+ }
23
+ map.addOverlay(new GTileLayerOverlay(layer));
24
+
25
+ Once this is done, google starts to request tiles from your server. To
26
+ implement the server you need to decode X (tile x), Y (tile y) and
27
+ Z (zoom level) to a bounding box of latitude and longitude so that you can
28
+ check what to draw in the tile that was requested. After you have fetched some
29
+ points you have to draw them in the Tile. This is where TileKit comes into play.
30
+ TileKit relies on gd2 a well known image rendering library (http://libgd.org).
31
+
32
+ # this example assumes that a request with x, y and z was done
33
+ # by the browser and saved into x, y, z
34
+
35
+ POI = TileKit::Icon.new("images/poi.png", [20, 20], [3, 20], [0, 0, 20, 17])
36
+ bounding_box = MapKit.bounding_box(x, y, z)
37
+
38
+ # search for points_of_interest in a bigger bouning box (grow by 10%)
39
+ points = DB.points(bounding_box.grow(10))
40
+
41
+ unless points.empty?
42
+ # cerate tile
43
+ tile = TileKit::Image.new(bounding_box)
44
+
45
+ # draw icons at point positions
46
+ for point in points do
47
+ tile.draw_icon(point, POI)
48
+ end
49
+
50
+ # return tile
51
+ return tile.png
52
+ end
53
+
54
+ To get an overview on the whole story checkout the sample application in the
55
+ example directory. It requires json, sequel, sqlite3 and sinatra.
@@ -0,0 +1,59 @@
1
+ require File.dirname(__FILE__) + "/mapkit"
2
+ require 'httparty'
3
+
4
+ # Class for searching with the google local search
5
+ class GoogleLocal
6
+ include HTTParty
7
+ base_uri "www.google.com"
8
+ default_params :hl => :de, :v => "1.0", :rsz => :large
9
+ format :json
10
+
11
+ # searches a term near point with sspn (span in degrees)
12
+ def self.search(term, point, sspn)
13
+ resp = get("/uds/GlocalSearch", :query => { :q => term,
14
+ :sll => point.join(","), :sspn => sspn.join(",") })
15
+ if resp["responseStatus"] == 200
16
+ resp["responseData"]["results"]
17
+ else
18
+ raise Exception.new("Error in Google request")
19
+ end
20
+ end
21
+
22
+ # just searches a term in a bounding box and returns points
23
+ def self.search_in_bounding_box(term, bounding_box)
24
+ crawl(term, bounding_box.center, bounding_box.sspn).map do |i|
25
+ MapKit::Point.new(i["lat"].to_f, i["lng"].to_f)
26
+ end
27
+ end
28
+
29
+ # searches a term near point with sspn (span in degrees)
30
+ def self.crawl(term, point, sspn)
31
+ print " - crawl for '#{term}' at #{point.inspect} within #{sspn.inspect} ("
32
+ count = 0
33
+ 4.times do |i|
34
+ data = get("/uds/GlocalSearch", :query => { :q => term, :start => i * 8,
35
+ :sll => point.join(","), :sspn => sspn.join(",") }).to_hash
36
+ if data["responseStatus"] == 200
37
+ data["responseData"]["results"].each do |row|
38
+ yield(row)
39
+ count += 1
40
+ end
41
+ else
42
+ raise Exception.new("Error in Google request")
43
+ end
44
+ print "."
45
+ end
46
+ print ") results: #{count}\n"
47
+ end
48
+
49
+ # searches a term near point with sspn (span in degrees) n times n
50
+ def self.crawl_region(term, point, span, n = 10, &block) # :yields: data
51
+ half_span = span / 2
52
+ n.times do |x|
53
+ n.times do |y|
54
+ point = [point[0] + x * half_span, point[1] + y * half_span]
55
+ crawl(term, point, [half_span, half_span], &block)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,184 @@
1
+ # Module to create tile for the google maps tile overlay
2
+ module MapKit
3
+ # consant for radiants
4
+ RADIANT = Math::PI / 180.0
5
+
6
+ # the size of tiles in google maps
7
+ TILE_SIZE = 256
8
+
9
+ # the constant earth radius in meters
10
+ EARTH_RADIUS = 6_378_137
11
+
12
+ # the min latitude based on the mercator projection
13
+ MIN_LATITUDE = -85.05112877
14
+
15
+ # the max latitude based on the mercator projection
16
+ MAX_LATITUDE = 85.05112877
17
+
18
+ # the min longitude based on the mercator projection
19
+ MIN_LONGITUDE = -180
20
+
21
+ # the max longitude based on the mercator projection
22
+ MAX_LONGITUDE = 180
23
+
24
+ # the resolution in meters per pixel
25
+ RESOLUTION = 2 * Math::PI * EARTH_RADIUS / TILE_SIZE
26
+
27
+ # version of MapKit
28
+ VERSION = "0.0.1"
29
+
30
+ # The class represents an lat/lng point
31
+ class Point
32
+ attr_accessor :lat, :lng
33
+
34
+ # initializes a point object using latitude and longitude
35
+ def initialize(lat, lng)
36
+ @lat, @lng = lat, lng
37
+ end
38
+
39
+ # returns true if point is in bounding_box, false otherwise
40
+ def in?(bounding_box)
41
+ top, left, bottom, right = bounding_box.coords
42
+ (left..right) === @lng && (top..bottom) === @lat
43
+ end
44
+
45
+ # returns relative x and y for point in bounding_box
46
+ def pixel(bounding_box)
47
+ top, left, bottom, right = bounding_box.coords
48
+ ws = (right - left) / TILE_SIZE
49
+ hs = (bottom - top) / TILE_SIZE
50
+ [((@lng - left) / ws).to_i, ((@lat - top) / hs).to_i]
51
+ end
52
+ end
53
+
54
+ # The class represents a bounding box specified by a top/left point and a
55
+ # bottom/right point (the coordinates can be pixels or degrees)
56
+ class BoundingBox
57
+ attr_accessor :top, :left, :bottom, :right, :zoom
58
+
59
+ # initialize the bounding box using the positions of two points and a
60
+ # optional zoom level
61
+ #
62
+ # top
63
+ # left o------+
64
+ # | |
65
+ # | |
66
+ # +------o right
67
+ # bottom
68
+ #
69
+ def initialize(top, left, bottom, right, zoom = nil)
70
+ @top, @left, @bottom, @right, @zoom = top, left, bottom, right, zoom
71
+ end
72
+
73
+ # returns array of [top, left, bottom, right]
74
+ def coords
75
+ [@top, @left, @bottom, @right]
76
+ end
77
+
78
+ # returns array of [width, height] of sspn
79
+ def sspn
80
+ [(@right - @left) / 2, (@bottom - @top) / 2]
81
+ end
82
+
83
+ # returns [lat, lnt] of bounding box
84
+ def center
85
+ [@left + (@right - @left) / 2, @top + (@bottom - @top) / 2]
86
+ end
87
+
88
+ # grow bounding box by percentage
89
+ def grow!(percent)
90
+ lng = percent * ((@right - @left) / 100)
91
+ lat = percent * ((@top - @bottom) / 100)
92
+ @top += lat
93
+ @left -= lng
94
+ @bottom -= lat
95
+ @right += lng
96
+ end
97
+
98
+ # grow bounding box by percentage and return new bounding box object
99
+ def grow(percent)
100
+ copy = self.clone
101
+ copy.grow!(percent)
102
+ copy
103
+ end
104
+ end
105
+
106
+ # return bounding box for passed tile coordinates tiles
107
+ def self.bounding_box(tile_x, tile_y, zoom)
108
+ top, left, bottom, right = tile_bounds(tile_x, tile_y, zoom)
109
+ BoundingBox.new(top, left, bottom, right, zoom)
110
+ end
111
+
112
+ # returns bounds [top, left, bottom, right] of the given tile
113
+ # in WGS-94 coordinates
114
+ def self.tile_bounds(tile_x, tile_y, zoom)
115
+ pixel_x, pixel_y = tile2pixel(tile_x, tile_y)
116
+ top, left = pixel2latlng(pixel_x, pixel_y, zoom)
117
+
118
+ pixel_x, pixel_y = tile2pixel(tile_x + 1, tile_y + 1)
119
+ bottom, right = pixel2latlng(pixel_x, pixel_y, zoom)
120
+
121
+ [top, left, bottom, right]
122
+ end
123
+
124
+ # returns [lat, lng] shifted using the passed pixels and zoom
125
+ def self.shift_latlng(lat, lng, shift_x, shift_y, zoom)
126
+ pixel_x, pixel_y = latlng2pixel(lat.to_f, lng.to_f, zoom)
127
+ pixel_x, pixel_y = pixel_x + shift_x, pixel_y + shift_y
128
+ pixel2latlng(pixel_x, pixel_y, zoom)
129
+ end
130
+
131
+ # returns pixel coordinates [x, y] based on the passed lat/lng WGS-84
132
+ # coordinates using the specified zoom level
133
+ def self.latlng2pixel(lat, lng, zoom)
134
+ lat = clip(lat.to_f, MIN_LATITUDE, MAX_LATITUDE)
135
+ lng = clip(lng.to_f, MIN_LONGITUDE, MAX_LONGITUDE)
136
+
137
+ x = (lng + 180.0) / 360.0
138
+ sin_lat = Math.sin(lat * RADIANT)
139
+ y = 0.5 - Math.log((1.0 + sin_lat) / (1.0 - sin_lat)) / (4.0 * Math::PI)
140
+ sx, sy = map_size(zoom)
141
+
142
+ pixel_x = clip(x * sx + 0.5, 0.0, sx - 1.0)
143
+ pixel_y = clip(y * sy + 0.5, 0.0, sy - 1.0)
144
+ [pixel_x.to_i, pixel_y.to_i]
145
+ end
146
+
147
+ # returns lat/lng WGS-84 coordinates [lat, lng] basedon the passed pixel
148
+ # coordinates using the specified zoom level
149
+ def self.pixel2latlng(pixel_x, pixel_y, zoom)
150
+ sx, sy = map_size(zoom)
151
+ x = clip(pixel_x.to_f, 0.0, sx - 1.0) / sx - 0.5
152
+ y = 0.5 - clip(pixel_y.to_f, 0.0, sy - 1.0) / sy
153
+
154
+ lat = 90.0 - 360.0 * Math.atan(Math.exp(-y * 2.0 * Math::PI)) / Math::PI
155
+ lng = 360.0 * x
156
+ [lat, lng]
157
+ end
158
+
159
+ # returns the passed value in case it is in the passed range or the
160
+ # bounding min or max value
161
+ def self.clip(val, min, max)
162
+ (val < min) ? min : (val > max) ? max : val
163
+ end
164
+
165
+ # returns coordinates of tiles using passed pixel coordinates
166
+ def self.pixel2tile(pixel_x, pixel_y)
167
+ [pixel_x / TILE_SIZE, pixel_y / TILE_SIZE]
168
+ end
169
+
170
+ # returns coordinates of pixels using passed tile coordinates
171
+ def self.tile2pixel(tile_x, tile_y)
172
+ [tile_x * TILE_SIZE, tile_y * TILE_SIZE]
173
+ end
174
+
175
+ # returns the size [x, y] of the map using the passed zoom level
176
+ def self.map_size(zoom)
177
+ [TILE_SIZE << zoom, TILE_SIZE << zoom]
178
+ end
179
+
180
+ # returns resolution in meters per pixel for passed zoom level
181
+ def self.resolution(zoom)
182
+ RESOLUTION / (2 ** zoom)
183
+ end
184
+ end
@@ -0,0 +1,57 @@
1
+ require File.dirname(__FILE__) + "/mapkit"
2
+ require "gd2"
3
+
4
+ module TileKit
5
+ class Icon
6
+ attr_reader :image, :size
7
+
8
+ def initialize(path, size, peak_position, clickable_area)
9
+ @image = GD2::Image.import("images/gas.png")
10
+ @size_x, @size_y = size
11
+ @peak_x, @peak_y = peak_position
12
+ @shift_x, @shift_y, @width, @height = clickable_area
13
+ end
14
+
15
+ def draw(canvas, x, y)
16
+ # position icon at peak point
17
+ x, y = x - @peak_x, y - @peak_y
18
+
19
+ # copy image
20
+ canvas.copy_from(@image, x, y, 0, 0, @size_x, @size_y)
21
+ end
22
+
23
+ def bounding_box(lat, lng, zoom)
24
+ top, left = MapKit.shift_latlng(lat, lng, @shift_x - @peak_x, @shift_y - @peak_y, zoom)
25
+ bottom, right = MapKit.shift_latlng(top, left, @width, @height, zoom)
26
+ MapKit::BoundingBox.new(top, left, bottom, right, zoom)
27
+ end
28
+ end
29
+
30
+ class Image
31
+ attr_reader :canvas, :bounding_box
32
+
33
+ def initialize(bounding_box)
34
+ @bounding_box = bounding_box
35
+
36
+ # create image canvas
37
+ @canvas = GD2::Image.new(MapKit::TILE_SIZE, MapKit::TILE_SIZE)
38
+
39
+ # make image transparent
40
+ @canvas.save_alpha = true
41
+ @canvas.draw do |context|
42
+ context.color = GD2::Color::TRANSPARENT
43
+ context.fill
44
+ end
45
+ end
46
+
47
+ # draw icon at position
48
+ def draw_icon(point, icon)
49
+ x, y = point.pixel(@bounding_box)
50
+ icon.draw(@canvas, x, y)
51
+ end
52
+
53
+ def png
54
+ @canvas.png
55
+ end
56
+ end
57
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mapkit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Vincent Landgraf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-10 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: httparty
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.5.2
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: gd2
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.1.1
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: rake
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: rspec
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ description: |-
56
+ MapKit is an set of helpers to assist building tiles for
57
+ the google maps web client
58
+ email:
59
+ - vincent.landgraf@inovex.de
60
+ executables: []
61
+
62
+ extensions: []
63
+
64
+ extra_rdoc_files: []
65
+
66
+ files:
67
+ - lib/google_local.rb
68
+ - lib/mapkit.rb
69
+ - lib/tilekit.rb
70
+ - LICENSE
71
+ - README.markdown
72
+ has_rdoc: true
73
+ homepage: http://github.com/threez/mapkit
74
+ licenses: []
75
+
76
+ post_install_message:
77
+ rdoc_options: []
78
+
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: "0"
86
+ version:
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 1.3.5
92
+ version:
93
+ requirements: []
94
+
95
+ rubyforge_project:
96
+ rubygems_version: 1.3.5
97
+ signing_key:
98
+ specification_version: 3
99
+ summary: MapKit helps rendering tiles for google maps
100
+ test_files: []
101
+