cloudmade 0.1.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.
@@ -0,0 +1,23 @@
1
+ require 'cloudmade'
2
+ include CloudMade
3
+
4
+ cm = CloudMade::Client.from_parameters('BC9A493B41014CAABB98F0471D759707')
5
+
6
+ puts 'Testing CloudMade Client'
7
+ puts "Geocoding"
8
+ puts "---------"
9
+ puts "Find"
10
+ puts cm.geocoding.find('Potsdamer Platz, Berlin, Germany')
11
+ puts "Find closest"
12
+ puts cm.geocoding.find_closest('pub', 50.9425, 6.9575)
13
+
14
+ puts "Routing\n-------\n"
15
+ puts cm.routing.route(
16
+ CloudMade::Point.new(47.25976, 9.58423),
17
+ CloudMade::Point.new(47.66117, 9.99882))
18
+
19
+ puts "Tiles\n-----\n"
20
+ tile = cm.tiles.get_tile(50.39947, 30.6479, 15)
21
+ file = File.new('my_tile.png', 'w')
22
+ file.write(tile)
23
+ file.close
@@ -0,0 +1,35 @@
1
+ #
2
+ # Copyright 2009 CloudMade.
3
+ #
4
+ # Licensed under the GNU Lesser General Public License, Version 3.0;
5
+ # You may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.gnu.org/licenses/lgpl-3.0.txt
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ #Raised when API key is not specified in Connection constructor
18
+ class APIKeyMissingError < Exception
19
+ end
20
+
21
+ #Raised when object was not found by geocoding service
22
+ class ObjectNotFound < Exception
23
+ end
24
+
25
+ #Raised when HTTP code other than 200 returned
26
+ class HTTPError < Exception
27
+ end
28
+
29
+ #Raised when specified route couldn't be found
30
+ class RouteNotFound < Exception
31
+ end
32
+
33
+ #Raised when geometry is malformed
34
+ class GeometryParseError < Exception
35
+ end
@@ -0,0 +1,144 @@
1
+ #
2
+ # Copyright 2009 CloudMade.
3
+ #
4
+ # Licensed under the GNU Lesser General Public License, Version 3.0;
5
+ # You may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.gnu.org/licenses/lgpl-3.0.txt
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ module CloudMade
18
+ require 'cgi'
19
+
20
+ # Class that is responsible for geocoding services of Cloudmade
21
+ class GeocodingService < Service
22
+
23
+ # Find objects that match given query. Returns GeoResults object.
24
+ #
25
+ # * +query+ Query by which objects should be searched
26
+ # * +options+ Additional options, contains
27
+ # results: How many results should be returned.
28
+ # skip: Number of results to skip from beginning.
29
+ # bbox: Bounding box in which objects should be searched.
30
+ # bbox_only: If set to False, results will be searched in the whole world if there are no results for a given bbox.
31
+ # return_geometry: If specified, adds geometry in returned results. Defaults to true.
32
+ #
33
+ def find(query, options = {})
34
+ options['results'] = 10 unless options.has_key? 'results'
35
+ options['skip'] = 0 unless options.has_key? 'skip'
36
+ options['bbox_only'] = true unless options.has_key? 'bbox_only'
37
+ options['return_geometry'] = true unless options.has_key? 'return_geometry'
38
+ request = "/find/#{CGI.escape(query)}.js?#{Service.to_url_params(options)}"
39
+
40
+ GeoResults.new(JSON.parse(connect request))
41
+ end
42
+
43
+ # Find closest object to a given point.
44
+ # For a list of available object types, see:
45
+ # http://www.cloudmade.com/developers/docs/geocoding-http-api/object_types
46
+ # Returns GeoResult object, that contain found objects.
47
+ # Raise ObjectNotFound: Raised when no object could be found in radius of 50 km from point.
48
+ #
49
+ # * +object_type+ Type of object, that should be searched.
50
+ # * +point+ Closest object to this point will be searched.
51
+ # * +options+ are
52
+ # * +return_geometry+ If specified, adds geometry in returned results. Defaults to true
53
+
54
+ # TODO: Modify lat, lon parameters to point
55
+ def find_closest(object_type, lat, lon, options = {})
56
+ lat_lon = "#{CGI.escape(lat.to_s + '+' + lon.to_s)}"
57
+ request = "/closest/#{object_type}/#{lat_lon}.js?#{Service.to_url_params(options)}"
58
+ geo_results = GeoResults.new(JSON.parse(connect request))
59
+ raise ObjectNotFound.new if (geo_results.results == nil or geo_results.results.size == 0)
60
+ return geo_results.results[0]
61
+ end
62
+
63
+ # :nodoc:
64
+ def url_template
65
+ return "http://#{@subdomain}.#{@connection.url}/#{@connection.api_key}/geocoding"
66
+ end
67
+ end
68
+
69
+ # Location of the object in geographical terms
70
+ class Location
71
+ # road: Road on which object is situated
72
+ attr_accessor :road
73
+ # city: City, where the object is situated
74
+ attr_accessor :city
75
+ # county: County, where the object is situated
76
+ attr_accessor :county
77
+ # country: Country in which the object is situated
78
+ attr_accessor :country
79
+ # postcode: Postcode, which corresponds to location of the object
80
+ attr_accessor :postcode
81
+
82
+ def initialize(data)
83
+ self.road = data['road']
84
+ self.city = data['city']
85
+ self.county = data['county']
86
+ self.country = data['country']
87
+ self.postcode = data['postcode']
88
+ end
89
+ end
90
+
91
+ # Parsed results of geocoding request
92
+ class GeoResults
93
+ # Founded results
94
+ attr_accessor :found
95
+ # List of found GeoResult objects
96
+ attr_accessor :results
97
+ # Bounds of result set
98
+ attr_accessor :bounds
99
+
100
+ def initialize(data)
101
+ self.found = Integer(data['found']) if data.has_key? 'found'
102
+ if (data['features'] != nil) then
103
+ self.results = data['features'].map { |feature_data| CloudMade::GeoResult.new(feature_data) }
104
+ end
105
+ self.bounds = CloudMade::BBox.from_coordinates(data['bounds']) if data.has_key? 'bounds'
106
+ end
107
+
108
+ def to_s
109
+ results.join(',') if results != nil
110
+ end
111
+ end
112
+
113
+ class GeoResult
114
+ # Id of the object
115
+ attr_accessor :id
116
+ # Geometry of the object
117
+ attr_accessor :geometry
118
+ # Centroid of the object
119
+ attr_accessor :centroid
120
+ # Bounds of result set
121
+ attr_accessor :bounds
122
+ # Properties of the object
123
+ attr_accessor :properties
124
+ # Location of the object
125
+ attr_accessor :location
126
+
127
+ def initialize(data)
128
+ self.id = data['id']
129
+ self.geometry = CloudMade::Geometry.parse(data['geometry'])
130
+ self.centroid = CloudMade::Geometry.parse(data['centroid'])
131
+ self.bounds = CloudMade::BBox.from_coordinates(data['bounds']) if data.has_key? 'bounds'
132
+ self.properties = data['properties']
133
+ if data.has_key? 'location'
134
+ self.location = Location.new(data['location'])
135
+ else
136
+ self.location = nil
137
+ end
138
+ end
139
+
140
+ def to_s
141
+ self.geometry.to_s
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,139 @@
1
+ #
2
+ # Copyright 2009 CloudMade.
3
+ #
4
+ # Licensed under the GNU Lesser General Public License, Version 3.0;
5
+ # You may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.gnu.org/licenses/lgpl-3.0.txt
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ module CloudMade
18
+ class Geometry
19
+
20
+ class << self
21
+ def parse(data)
22
+ return nil if data == nil
23
+ case data['type'].downcase
24
+ when 'point' then return Point.new(data['coordinates'])
25
+ when 'line' then return Line.new(data['coordinates'])
26
+ when 'multilinestring' then return MultiLine.new(data['coordinates'])
27
+ when 'polygon' then return Polygon.new(data['coordinates'])
28
+ when 'multipolygon' then return MultiPolygon.new(data['coordinates'])
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ class Point < Geometry
35
+ attr_accessor :lat
36
+ attr_accessor :lon
37
+
38
+ # Posible initializers are
39
+ # Point.new(1, -1)
40
+ # Point.new([1, -1])
41
+ def initialize(*args)
42
+ if args.size == 1
43
+ @lat = args[0][0]
44
+ @lon = args[0][1]
45
+ elsif args.size == 2
46
+ @lat = args[0]
47
+ @lon = args[1]
48
+ end
49
+ end
50
+
51
+ def ==(point)
52
+ return (point.lat == @lat and point.lon == @lon)
53
+ end
54
+
55
+ def to_s
56
+ "Point(#{@lat},#{@lon})"
57
+ end
58
+
59
+ def to_latlon
60
+ "#{@lat},#{@lon}"
61
+ end
62
+ end
63
+
64
+ class Line < Geometry
65
+ attr_accessor :points
66
+
67
+ def initialize(coords)
68
+ @points = coords.map { |latlng| Point.new(latlng) }
69
+ end
70
+
71
+ def to_s
72
+ "Line(#{@points.join(',')})"
73
+ end
74
+ end
75
+
76
+
77
+ class MultiLine < Geometry
78
+ attr_accessor :lines
79
+
80
+ def initialize(coords)
81
+ @lines = coords.map { |line_coords| Line.new(line_coords) }
82
+ end
83
+
84
+ def to_s
85
+ "MultiLine(#{@lines.join(',')})"
86
+ end
87
+ end
88
+
89
+ class Polygon < Geometry
90
+ attr_accessor :border_line
91
+ attr_accessor :holes
92
+
93
+ def initialize(coords)
94
+ @border_line = Line.new(coords[0])
95
+ @holes = coords.slice(1, coords.length).map { |line_coords| Line.new(line_coords) }
96
+ end
97
+
98
+ def to_s
99
+ "Polygon(#{@border_line} - (#{@holes.join(',')}))"
100
+ end
101
+ end
102
+
103
+ class MultiPolygon < Geometry
104
+ attr_accessor :polygons
105
+
106
+ def initialize(coords)
107
+ @polygons = coords.map { |poly_coords| Polygon.new(poly_coords) }
108
+ end
109
+
110
+ def to_s
111
+ "MultiPolygon(#{@polygons.join(',')})"
112
+ end
113
+ end
114
+
115
+ class BBox < Geometry
116
+ attr_accessor :points
117
+
118
+ def initialize(points)
119
+ self.points = points
120
+ end
121
+
122
+ class << self
123
+ def from_points(points)
124
+ return BBox.new(points)
125
+ end
126
+
127
+ def from_coordinates(coords)
128
+ point1 = Point.new(coords[0][0], coords[0][1])
129
+ point2 = Point.new(coords[1][0], coords[1][1])
130
+ return BBox.new([point1, point2])
131
+ end
132
+ end
133
+
134
+ def ==(bbox)
135
+ return (bbox.points[0] == self.points[0] and bbox.points[1] = self.points[1] or
136
+ bbox.points[0] == self.points[1] and bbox.points[1] = self.points[0])
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,20 @@
1
+ #
2
+ # Copyright 2009 CloudMade.
3
+ #
4
+ # Licensed under the GNU Lesser General Public License, Version 3.0;
5
+ # You may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.gnu.org/licenses/lgpl-3.0.txt
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ module CloudMade
18
+ class LocationsService < Service
19
+ end
20
+ end
@@ -0,0 +1,145 @@
1
+ #
2
+ # Copyright 2009 CloudMade.
3
+ #
4
+ # Licensed under the GNU Lesser General Public License, Version 3.0;
5
+ # You may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.gnu.org/licenses/lgpl-3.0.txt
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'cgi'
18
+ require 'json'
19
+
20
+ module CloudMade
21
+
22
+ #Class corresponding for CloudMade's routing service
23
+ class RoutingService < Service
24
+
25
+ ROUTE_TYPES = ['car', 'foot', 'bicycle']
26
+ OUTPUT_FORMATS = ['js', 'json', 'gpx']
27
+ STATUS_OK = 0
28
+ STATUS_ERROR = 1
29
+ EARTHS_DIRECTIONS = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"]
30
+ TURN_TYPE = ["C", "TL", "TSLL", "TSHL", "TR", "TSLR", "TSHR", "U"]
31
+
32
+ # Build route. Returns route that was found, instance of CloudMade::Route
33
+ # * +start_point+ Starting point
34
+ # * +end_point+ Ending point
35
+ # * +route_type+ Type of route, e.g. 'car', 'foot', etc.
36
+ # * +transit_points+ List of points route must visit before
37
+ # reaching end. Points are visited in the same order they are
38
+ # specified in the sequence.
39
+ # * +route_type_modifier+ Modifier of the route type
40
+ # * +lang+ Language code in conformance to `ISO 3166-1 alpha-2` standard
41
+ # * +units+ Measure units for distance calculation
42
+ def route(start_point, end_point, transit_points = nil, route_type = 'car', lang = 'en', route_type_modifier = nil)
43
+ transit_points = ",[#{transit_points.map {|point| point.to_latlon}.join(',')}]" if not transit_points == nil
44
+ route_type_modifier = "/#{route_type_modifier}" if not route_type_modifier == nil
45
+ url = "/#{start_point.to_latlon}#{transit_points},#{end_point.to_latlon}/#{route_type}#{route_type_modifier}.js?lang=#{lang}&units=km"
46
+ return Route.new(JSON.parse(connect url))
47
+ end
48
+
49
+ #:nodoc:
50
+ def url_template
51
+ return "http://#{@subdomain}.#{@connection.url}/#{@connection.api_key}/api/0.3"
52
+ end
53
+ end
54
+
55
+
56
+ # Statistics of the route
57
+ class RouteSummary
58
+ attr_accessor :total_distance
59
+ attr_accessor :total_time
60
+ attr_accessor :start_point
61
+ attr_accessor :end_point
62
+ attr_accessor :transit_points
63
+
64
+ def initialize(summary)
65
+ self.total_distance = Float(summary['total_distance'])
66
+ self.total_time = Float(summary['total_time'])
67
+ self.start_point = summary['start_point']
68
+ self.end_point = summary['end_point']
69
+ self.transit_points = summary['transit_points']
70
+ end
71
+ end
72
+
73
+ # Wrapper around raw data being returned by routing service
74
+ class Route
75
+
76
+ # List of route instructions
77
+ attr_accessor :instructions
78
+ # Statistical info about the route
79
+ attr_accessor :summary
80
+ # Geometry of route
81
+ attr_accessor :geometry
82
+ #Version of routing HTTP API
83
+ attr_accessor :version
84
+ # Status of response
85
+ attr_accessor :status
86
+ attr_accessor :status_message
87
+
88
+ STATUS_OK = 0
89
+ STATUS_ERROR = 1
90
+
91
+ def initialize(data)
92
+ begin
93
+ self.status = data['status'].to_i
94
+ self.instructions = data['route_instructions'].map { |instruction_data| RouteInstruction.new(instruction_data) }
95
+ self.summary = RouteSummary.new(data['route_summary'])
96
+ self.geometry = Line.new(data['route_geometry'])
97
+ self.version = data['version']
98
+ self.status_message = data['status_message']
99
+ rescue
100
+ raise RouteNotFound
101
+ end
102
+ end
103
+
104
+ def to_s
105
+ self.instructions.to_s
106
+ end
107
+ end
108
+
109
+ # Instructions on route passing
110
+ class RouteInstruction
111
+
112
+ # Text instruction
113
+ attr_reader :instruction
114
+ # Length of the segment in meters
115
+ attr_reader :length
116
+ # Index of the first point of the segment
117
+ attr_reader :position
118
+ # Estimated time required to travel the segment in seconds
119
+ attr_reader :time
120
+ # Length of the segments in specified units
121
+ attr_reader :length_caption
122
+ # Earth direction
123
+ attr_reader :earth_direction
124
+ # North-based azimuth
125
+ attr_reader :azimuth
126
+ # Code of the turn type
127
+ attr_reader :turn_type
128
+ # Angle in degress of the turn between two segments
129
+ attr_reader :turn_angle
130
+
131
+ def initialize(data)
132
+ @instruction = data[0]
133
+ @length = Float(data[1])
134
+ @position = Integer(data[2])
135
+ @time = Integer(data[3])
136
+ @length_caption = data[4]
137
+ @earth_direction = data[5]
138
+ @azimuth = Float(data[6])
139
+ if data.length == 9
140
+ @turn_type = data[7]
141
+ @turn_angle = data[8]
142
+ end
143
+ end
144
+ end
145
+ end