source: subversion/sites/rails_port/lib/osm.rb @ 3252

Last change on this file since 3252 was 3252, checked in by dankarran, 12 years ago

Adding a guid element to feed items, replicates URL.

File size: 8.5 KB
Line 
1module OSM
2
3  # This piece of magic reads a GPX with SAX and spits out
4  # lat/lng and stuff
5  #
6  # This would print every latitude value:
7  #
8  # gpx = OSM:GPXImporter.new('somefile.gpx')
9  # gpx.points {|p| puts p['latitude']}
10
11  require 'time'
12  require 'rexml/parsers/sax2parser'
13  require 'rexml/text'
14  require 'xml/libxml'
15  require 'RMagick'
16
17  class Mercator
18    include Math
19
20    def initialize(lat, lon, degrees_per_pixel, width, height)
21      #init me with your centre lat/lon, the number of degrees per pixel and the size of your image
22      @clat = lat
23      @clon = lon
24      @degrees_per_pixel = degrees_per_pixel
25      @width = width
26      @height = height
27      @dlon = width / 2 * degrees_per_pixel
28      @dlat = height / 2 * degrees_per_pixel  * cos(@clat * PI / 180)
29
30      @tx = xsheet(@clon - @dlon)
31      @ty = ysheet(@clat - @dlat)
32
33      @bx = xsheet(@clon + @dlon)
34      @by = ysheet(@clat + @dlat)
35
36    end
37
38    #the following two functions will give you the x/y on the entire sheet
39
40    def kilometerinpixels
41      return 40008.0  / 360.0 * @degrees_per_pixel
42    end
43
44    def ysheet(lat)
45      log(tan(PI / 4 +  (lat  * PI / 180 / 2)))
46    end
47
48    def xsheet(lon)
49      lon
50    end
51
52    #and these two will give you the right points on your image. all the constants can be reduced to speed things up. FIXME
53
54    def y(lat)
55      return @height - ((ysheet(lat) - @ty) / (@by - @ty) * @height)
56    end
57
58    def x(lon)
59      return  ((xsheet(lon) - @tx) / (@bx - @tx) * @width)
60    end
61  end
62
63
64  class GPXImporter
65    # FIXME swap REXML for libXML
66    attr_reader :possible_points
67    attr_reader :actual_points
68    attr_reader :tracksegs
69
70    def initialize(filename)
71      @possible_points = 0
72      @actual_points = 0
73      @tracksegs = 0
74      @points = []
75
76      file = File.new(filename)
77      parser = REXML::Parsers::SAX2Parser.new( file )
78
79      lat = -1
80      lon = -1
81      ele = -1
82      date = Time.now();
83      gotlatlon = false
84      gotele = false
85      gotdate = false
86
87      parser.listen( :start_element,  %w{ trkpt }) do |uri,localname,qname,attributes| 
88        lat = attributes['lat'].to_f
89        lon = attributes['lon'].to_f
90        gotlatlon = true
91        @possible_points += 1
92      end
93
94      parser.listen( :characters, %w{ ele } ) do |text|
95        ele = text
96        gotele = true
97      end
98
99      parser.listen( :characters, %w{ time } ) do |text|
100        if text && text != ''
101          date = Time.parse(text)
102          gotdate = true
103        end
104      end
105
106      parser.listen( :end_element, %w{ trkseg } ) do |uri, localname, qname|
107        @tracksegs += 1
108      end
109
110      parser.listen( :end_element, %w{ trkpt } ) do |uri,localname,qname|
111        if gotlatlon && gotdate
112          ele = '0' unless gotele
113          if lat < 90 && lat > -90 && lon > -180 && lon < 180
114            @actual_points += 1
115            @points.push(Hash['latitude' => lat,'longitude' => lon,'timestamp' => date,'altitude' => ele,'segment' => @tracksegs])
116          end
117        end
118        gotlatlon = false
119        gotele = false
120        gotdate = false
121      end
122      parser.parse
123    end
124
125    def points
126      @points.each { |p| yield p }
127    end
128
129    def get_picture(min_lat, min_lon, max_lat, max_lon, num_points)
130      #puts "getting picfor bbox #{min_lat},#{min_lon} - #{max_lat},#{max_lon}"
131      frames = 10
132      width = 250
133      height = 250
134      rat= Math.cos( ((max_lat + min_lat)/2.0) /  180.0 * 3.141592)
135      proj = OSM::Mercator.new((min_lat + max_lat) / 2, (max_lon + min_lon) / 2, (max_lat - min_lat) / width / rat, width, height)
136
137      images = []
138
139      frames.times do
140        gc =  Magick::Draw.new
141        gc.stroke_linejoin('miter')
142        gc.stroke('#FFFFFF')
143        gc.fill('#FFFFFF')
144        gc.rectangle(0,0,width,height)
145        gc.stroke_width(1)
146        images << gc
147      end
148
149      oldpx = 0.0
150      oldpy = 0.0
151
152      first = true
153
154      m = 0
155      mm = 0
156      points do |p|
157        px = proj.x(p['longitude'])
158        py = proj.y(p['latitude'])
159        frames.times do |n|
160          images[n].stroke_width(1)
161          images[n].stroke('#BBBBBB')
162          images[n].fill('#BBBBBB')
163        #  puts "A #{px},#{py} - #{oldpx},#{oldpy}"
164          images[n].line(px, py, oldpx, oldpy ) unless first
165        end
166        images[mm].stroke_width(3)
167        images[mm].stroke('#000000')
168        images[mm].fill('#000000')
169        images[mm].line(px, py, oldpx, oldpy ) unless first
170      #  puts "B #{px},#{py} - #{oldpx},#{oldpy}"
171        m +=1
172        if m > num_points.to_f / frames.to_f * (mm+1)
173          mm += 1
174        end
175        first = false
176        oldpy = py
177        oldpx = px
178      end
179
180      il = Magick::ImageList.new
181
182      frames.times do |n|
183        canvas = Magick::Image.new(width, height) {
184          self.background_color = 'white'
185        }
186        begin
187          images[n].draw(canvas)
188        rescue ArgumentError
189        end
190        canvas.format = 'GIF'
191        il << canvas
192      end
193
194      il.delay = 50
195      il.format = 'GIF'
196      return il.to_blob
197    end
198
199    def get_icon(min_lat, min_lon, max_lat, max_lon)
200      puts "getting icon for bbox #{min_lat},#{min_lon} - #{max_lat},#{max_lon}"
201      width = 50
202      height = 50
203      rat= Math.cos( ((max_lat + min_lat)/2.0) /  180.0 * 3.141592)
204      proj = OSM::Mercator.new((min_lat + max_lat) / 2, (max_lon + min_lon) / 2, (max_lat - min_lat) / width / rat, width, height)
205
206      images = []
207
208      gc =  Magick::Draw.new
209      gc.stroke_linejoin('miter')
210
211      oldpx = 0.0
212      oldpy = 0.0
213
214      first = true
215
216      gc.stroke_width(1)
217      gc.stroke('#000000')
218      gc.fill('#000000')
219
220      points do |p|
221        px = proj.x(p['longitude'])
222        py = proj.y(p['latitude'])
223        gc.line(px, py, oldpx, oldpy ) unless first
224       # puts "C #{px},#{py} - #{oldpx},#{oldpy}"
225        first = false
226        oldpy = py
227        oldpx = px
228      end
229
230      canvas = Magick::Image.new(width, height) {
231        self.background_color = 'white'
232      }
233      begin
234        gc.draw(canvas)
235      rescue ArgumentError
236      end
237      canvas.format = 'GIF'
238      return canvas.to_blob
239    end
240
241  end
242
243  class GeoRSS
244    def initialize(feed_title='OpenStreetMap GPS Traces', feed_description='OpenStreetMap GPS Traces', feed_url='http://www.openstreetmap.org/traces/')
245      @doc = XML::Document.new
246      @doc.encoding = 'UTF-8' 
247     
248      rss = XML::Node.new 'rss'
249      @doc.root = rss
250      rss['version'] = "2.0"
251      rss['xmlns:geo'] = "http://www.w3.org/2003/01/geo/wgs84_pos#"
252      @channel = XML::Node.new 'channel'
253      rss << @channel
254      title = XML::Node.new 'title'
255      title <<  feed_title
256      @channel << title
257      description_el = XML::Node.new 'description'
258      @channel << description_el
259
260      description_el << feed_description
261      link = XML::Node.new 'link'
262      link << feed_url
263      @channel << link
264      image = XML::Node.new 'image'
265      @channel << image
266      url = XML::Node.new 'url'
267      url << 'http://www.openstreetmap.org/feeds/mag_map-rss2.0.png'
268      image << url
269      title = XML::Node.new 'title'
270      title << "OpenStreetMap"
271      image << title
272      width = XML::Node.new 'width'
273      width << '100'
274      image << width
275      height = XML::Node.new 'height'
276      height << '100'
277      image << height
278      link = XML::Node.new 'link'
279      link << feed_url
280      image << link
281    end
282
283    def add(latitude=0, longitude=0, title_text='dummy title', url='http://www.example.com/', description_text='dummy description', timestamp=Time.now)
284      item = XML::Node.new 'item'
285
286      title = XML::Node.new 'title'
287      item << title
288      title << title_text
289      link = XML::Node.new 'link'
290      link << url
291      item << link
292
293      guid = XML::Node.new 'guid'
294      guid << url
295      item << guid
296
297      description = XML::Node.new 'description'
298      description << description_text
299      item << description
300
301      pubDate = XML::Node.new 'pubDate'
302      pubDate << timestamp.to_s(:rfc822)
303      item << pubDate
304
305      if latitude
306        lat_el = XML::Node.new 'geo:lat'
307        lat_el << latitude.to_s
308        item << lat_el
309      end
310
311      if longitude
312        lon_el = XML::Node.new 'geo:long'
313        lon_el << longitude.to_s
314        item << lon_el
315      end
316
317      @channel << item
318    end
319
320    def to_s
321      return @doc.to_s
322    end
323  end
324
325  class API
326    def get_xml_doc
327      doc = XML::Document.new
328      doc.encoding = 'UTF-8' 
329      root = XML::Node.new 'osm'
330      root['version'] = API_VERSION
331      root['generator'] = 'OpenStreetMap server'
332      doc.root = root
333      return doc
334    end
335  end
336end
Note: See TracBrowser for help on using the repository browser.