source: subversion/applications/rendering/osmps/osmps.rb @ 2667

Last change on this file since 2667 was 2322, checked in by matthewnc, 13 years ago

utils/osmps PostScript? Renderer

draw white background around text (creates an outline font) so
that it stands out better, especially on footpaths/cycleways

  • Property svn:executable set to *
File size: 25.3 KB
Line 
1#! /usr/bin/ruby
2#
3# OSMPS
4#
5# OpenStreetMap .osm to PostScript renderer
6#
7# Copyright (c) Matthew Newton, 2007
8#
9#-------------------------------------------------------------------------------
10#   This program is free software; you can redistribute it and/or modify
11#   it under the terms of the GNU General Public License as published by
12#   the Free Software Foundation; either version 2 of the License, or
13#   (at your option) any later version.
14#
15#   This program is distributed in the hope that it will be useful,
16#   but WITHOUT ANY WARRANTY; without even the implied warranty of
17#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18#   GNU General Public License for more details.
19#
20#   You should have received a copy of the GNU General Public License
21#   along with this program; if not, write to the Free Software
22#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23#-------------------------------------------------------------------------------
24#
25#   As an additional exception, PostScript code contained within,
26#   and output by, this program may be freely copied and modified
27#   without restriction.
28#
29#   The PostScript pathtext code is based on Sample Code from the book
30#     "PostScript Language Tutorial and Cookbook"
31#   Copyright (c) Adobe Systems Inc. 1985 and is included in this
32#   software within the terms of the Adobe licence found at
33#   http://partners.adobe.com/public/developer/ps/eula_submit.jsp?eula_name=ps_eula
34#
35#-------------------------------------------------------------------------------
36# This source is best viewed with a folding editor, such as Vim ;-)
37
38require 'xml/libxml'
39
40PSResource = <<EOR# {{{
41%%BeginResource: osmps
42/bd {bind def} bind def
43/pathtomarkdict 1 dict def
44/pathtomark {
45% move all but first pair into array
46  counttomark 2 sub array astore
47% pull the first coords to the top (mark array x y)
48  3 1 roll
49% perform transformation, and moveto
50  newpath
51  exch x exch y
52  moveto
53% lineto for each other pair
54  pathtomarkdict begin
55  /xpos false def
56  {
57    xpos false eq {
58      /xpos exch x def
59    }
60    {
61      y xpos exch
62      lineto
63      /xpos false def
64    } ifelse
65  } forall
66  end
67  pop
68} def
69/reversepathtomark
70  { newpath exch x exch y moveto
71    counttomark 2 sub -2 0
72    { pop exch x exch y lineto
73    } for
74    pop
75  } def
76/pathisrtol
77  { counttomark 1 sub index
78    2 index gt
79  } def
80/pathcopy { counttomark 1 add copy } def
81/area { pathtomark fill } def
82/line { pathtomark stroke } def
83/cline { pathtomark closepath stroke } def
84/c {
85255 div 3 1 roll
86255 div 3 1 roll
87255 div 3 1 roll
88setrgbcolor
89} def
90/lw {setlinewidth} bd
91/gs {gsave} bd
92/gr {grestore} bd
93% node
94/n { 0.2 0 360 arc fill } bd
95% segment
96/s { 4 2 roll moveto lineto } bd
97% 0.3 setlinewidth
98% 0.7 setgray
99
100%------------------------------------------------------------
101% Tweaked PathText code from Adobe PostScript Cookbook
102/pathtextdict 26 dict def
103/pathtext
104  { pathtextdict begin
105  /charbaseoffset 0.15 def
106  /offset exch def
107  /str exch def
108  /pathdist 0 def
109  /setdist offset def
110  /charcount 0 def
111  gsave
112    flattenpath
113    {movetoproc}  {linetoproc}
114    {curvetoproc} {closepathproc}
115    pathforall
116  grestore
117  newpath
118  end
119  } def
120 
121pathtextdict begin
122/movetoproc
123  { /newy exch def /newx exch def
124  /firstx newx def /firsty newy def
125  %/ovr 0 def   <- bug in Adobe's original code
126  /ovr offset def
127  newx newy transform
128  /cpy exch def /cpx exch def
129  } def
130 
131/linetoproc
132  { /oldx newx def /oldy newy def
133  /newy exch def /newx exch def
134  /dx newx oldx sub def
135  /dy newy oldy sub def
136  /dist dx dup mul dy dup mul add sqrt def
137  dist 0 ne
138    { /dsx dx dist div ovr mul def
139      /dsy dy dist div ovr mul def
140      oldx dsx add oldy dsy add transform
141      /cpy exch def /cpx exch def
142      /pathdist pathdist dist add def
143    { setdist pathdist le
144      { charcount str length lt
145      {setchar} {exit} ifelse }
146      { /ovr setdist pathdist sub def
147      exit }
148      ifelse
149    } loop
150      } if
151  } def
152 
153/curvetoproc
154  { (ERROR: No curveto's after flattenpath!) print
155  } def
156 
157/closepathproc
158  { firstx firsty linetoproc
159  firstx firsty movetoproc
160  } def
161 
162/setchar
163  { /char str charcount 1 getinterval def
164  /charcount charcount 1 add def
165  /charwidth char stringwidth pop def
166  gsave
167    cpx cpy itransform translate
168    dy dx atan rotate
169    0 charbaseoffset neg moveto
170    char show
171    0 charbaseoffset rmoveto
172    currentpoint transform
173    /cpy exch def /cpx exch def
174  grestore
175  /setdist setdist charwidth add def
176  } def
177end
178% End of code derived from Adobe source
179%------------------------------------------------------------
180
181%------------------------------------------------------------
182% Calculate the length of the current graphics path
183% - pathlen <length>
184%
185/pathlendict 7 dict def
186/pathlen
187  { pathlendict begin
188  /length 0 def
189  gsave
190    flattenpath
191    {pathlenmt} {pathlenlt}
192    {pathlenct} {pathlencp}
193    pathforall
194  grestore
195  newpath
196  length
197  end
198  } def
199
200pathlendict begin
201/pathlenmt
202  { /newy exch def
203    /newx exch def
204    /firstx newx def
205    /firsty newy def
206  } def
207
208/pathlenlt
209  { /oldx newx def
210    /oldy newy def
211    /newy exch def
212    /newx exch def
213    /length newx oldx sub dup mul
214            newy oldy sub dup mul
215            add sqrt
216            length add def
217  } def
218
219/pathlenct
220  { (This should never happen!) print
221  } def
222
223/pathlencp
224  { firstx firsty pathlenlt
225  } def
226end
227
228%------------------------------------------------------------
229% Draw in a road name, central to the given path, but only
230% if it will actually fit.
231% mark <x1> <y1> <x2> <y2> <xn> <yn> (text) roadname -
232%
233/roadnamedict 3 dict def
234/roadname
235  { roadnamedict begin
236    /text exch def
237    pathisrtol
238      { reversepathtomark }
239      { pathtomark }
240      ifelse
241    gsave
242    /plen pathlen def
243    RoadNameCoreFont
244    /tlen text stringwidth pop def
245    grestore
246    tlen 0.0 gt plen 0.0 gt and
247    plen tlen gt and
248    { gsave
249      1 1 1 setrgbcolor
250      RoadNameOutlineFont
251      text
252      plen 2 div tlen 2 div sub
253      pathtext
254      grestore
255      gsave
256      0 0 0 setrgbcolor
257      RoadNameCoreFont
258      text
259      plen 2 div tlen 2 div sub
260      pathtext
261      grestore
262    } if
263    end
264  } def
265
266/RoadNameFont
267  { /Helvetica-Bold findfont
268  } def
269
270/RoadNameOutline
271RoadNameFont
272dup maxlength 1 add
273exch /UniqueID known not { 1 add } if
274dict def
275
276RoadNameFont
277{ exch dup /FID ne
278  { exch RoadNameOutline 3 1 roll put }
279  { pop pop }
280  ifelse
281} forall
282
283RoadNameOutline
284dup /PaintType 2 put
285dup /StrokeWidth 300 put
286dup /FontName /RoadNameOutline put
287dup /UniqueID 1001 put
288/RoadNameOutline exch definefont pop
289
290/RoadNameCoreFont
291  { RoadNameFont
292    0.5 scalefont
293    setfont
294  } def
295
296/RoadNameOutlineFont
297  { /RoadNameOutline findfont
298    0.5 scalefont
299    setfont
300  } def
301
302RoadNameCoreFont
303
304%%EndResource
305EOR
306# }}}
307class Style# {{{
308  def initialize# {{{
309    @match = {}
310    @style = ""
311    @type = :path
312    @drawps = {}
313  end
314
315# }}}
316  def addtag(k, va)# {{{
317    if va == nil
318      @match[k] = nil
319    end
320    if not @match.has_key?(k)
321      @match[k] = []
322    end
323    if @match[k] != nil
324      arr = va
325      if va.class == String
326        arr = [va]
327      end
328      arr.each do |v|
329        @match[k].push(v)
330      end
331    end
332  end
333
334# }}}
335  def matchtags(tags)# {{{
336    @match.keys.each do |m|
337      if not tags.has_key?(m)
338        return false
339      end
340      if @match[m] != nil
341        if not @match[m].include?(tags[m])
342          return false
343        end
344      end
345    end
346    true
347  end
348
349# }}}
350  def setarea# {{{
351    @type = :area
352  end
353
354# }}}
355  def setpath# {{{
356    @type = :path
357  end
358
359# }}}
360  def setnode# {{{
361    @type = :node
362  end
363 
364# }}}
365  def area?# {{{
366    @type == :area
367  end
368
369# }}}
370  def adddrawps(layer, ps)# {{{
371    if not @drawps.has_key?(layer)
372      @drawps[layer] = []
373    end
374    @drawps[layer].push(ps)
375  end
376 
377# }}}
378  def adddrawtagstring(layer, tagname)# {{{
379    if not @drawps.has_key?(layer)
380      @drawps[layer] = []
381    end
382    @drawps[layer].push("%tag #{tagname}")
383  end
384 
385# }}}
386  def layers# {{{
387    @drawps.keys
388  end
389 
390# }}}
391  def length# {{{
392    @drawps.length
393  end
394
395# }}}
396  def ps(layer, tags)# {{{
397  # return "" if layer is out of range
398    if @drawps[layer] == nil
399      return ""
400    end
401    ps = ""
402    @drawps[layer].each do |d|
403      s = d
404      if d[0].chr == "%"
405        cmd = d.split(" ")
406        if cmd[0] == "%tag"
407          s = tags.has_key?(cmd[1]) ? "(#{tags[cmd[1]]})" : "()"
408        end
409      end
410      ps += s + "\n"
411    end
412#    @drawps[num].join(" ") + "\n"
413    ps
414  end
415
416# }}}
417  def to_s# {{{
418    @drawps.join(" ") + "\n"
419  end
420
421# }}}
422end #}}}
423class Tags #{{{
424  def initialize(tags)# {{{
425    @tags = {}
426    self.append(tags)
427    @usecount = 0
428    @styles = nil
429    @area = false
430  end
431
432# }}}
433  def append(tags)# {{{
434    tags.keys.each do |t|
435      @tags[t] = tags[t]
436    end
437  end
438
439# }}}
440  def tags# {{{
441    @tags
442  end
443
444# }}}
445  def length# {{{
446    @tags.length
447  end
448
449# }}}
450  def area?# {{{
451    @area
452  end
453
454# }}}
455  def renderprepare(styles)# {{{
456    @styles = []
457    styles.each do |s|
458      if s.matchtags(@tags)
459        @styles.push(s)
460        if s.area?
461          @area = true
462        end
463      end
464    end
465  end
466
467# }}}
468  def renderclear# {{{
469    @styles = nil
470  end
471
472# }}}
473  def renderpre# {{{
474    "gs\n"
475  end
476
477# }}}
478  def render# {{{
479    ps = ""
480    @styles.each do |s|
481      ps += s.to_s
482    end
483    ps
484  end
485
486# }}}
487  def renderlayers# {{{
488    layers = {}
489    @styles.each do |s|
490      s.layers.each do |l|
491        layers[l] = 1
492      end
493    end
494    layers.keys
495  end
496
497# }}}
498  def renderlayer(l)# {{{
499    ps = ""
500    @styles.each do |s|
501      ps += s.ps(l, @tags)
502    end
503    ps
504  end
505
506# }}}
507  def renderpost# {{{
508    "gr\n"
509  end
510
511# }}}
512  def renderlength# {{{
513    length = 0
514    @styles.each { |s| length = length > s.length ? length : s.length }
515    length
516  end
517
518# }}}
519  def inc# {{{
520    @usecount += 1
521  end
522
523# }}}
524  def dec# {{{
525    @usecount -= 1
526  end
527
528# }}}
529  def used# {{{
530    @usecount
531  end
532
533# }}}
534  def to_s# {{{
535    "tags(" + @tags.map{|t| t[0]+"="+t[1]}.join(",") + ")"
536  end
537
538# }}}
539end #}}}
540class MapObject #{{{
541  def initialize
542    @tags = nil
543    @graph = nil
544    @osmid = nil
545  end
546
547  def setgraph(g)
548    if @graph != nil
549      raise "error"
550    end
551    @graph = g
552  end
553
554  def settags(t)
555    @tags = t
556  end
557
558  def setosmid(id)
559    @osmid = id.to_i
560  end
561
562  def osmid
563    @osmid
564  end
565
566  def addtags(t)
567    if @tags == nil
568      return settags(t)
569    end
570    if @graph != nil
571      return @graph.addtags(self, t)
572    end
573    @tags = @tags + t
574  end
575
576  def tags
577    @tags
578  end
579end #}}}
580class Node < MapObject #{{{
581  def initialize(lon, lat)# {{{
582    @lon = lon.to_f
583    @lat = lat.to_f
584    @segments = []
585    super()
586  end
587
588  # }}}
589  def addsegment(seg)# {{{
590    if seg != nil
591      if not @segments.include?(seg)
592        @segments.push(seg)
593      end
594    end
595  end
596
597  # }}}
598  def segments# {{{
599    @segments
600  end
601
602  # }}}
603  def x# {{{
604    (@lon + 180) / 360
605  end
606
607  # }}}
608  def y# {{{
609    ly = projf(85.0511)
610    y = projf(@lat)
611    (ly - y) / (2 * ly)
612  end
613
614  # }}}
615  def to_s# {{{
616    #"node(" + @lon.to_s + "," + @lat.to_s + ")"
617    "node(#" + @osmid.to_s + ")"
618  end
619 
620  # }}}
621
622  private
623
624  def projf(la)# {{{
625    la = la * (3.1415926535897932384/180)
626    Math.log10(Math.tan(la) + (1/Math.cos(la)))
627  end
628
629# }}}
630end
631
632#}}}
633class Segment < MapObject #{{{
634  def initialize(from, to)
635    @fnode = from
636    @tnode = to
637    super()
638    @fnode.addsegment(self)
639    @tnode.addsegment(self)
640  end
641
642  def from
643    @fnode
644  end
645
646  def to
647    @tnode
648  end
649
650  def to_s
651    "segment(#" + @osmid.to_s + ", " + @fnode.to_s + "->" + @tnode.to_s + ")"
652  end
653end
654
655#}}}
656class Path < MapObject# {{{
657  def initialize(segments)# {{{
658    super()
659    @segments = segments
660    @tags = segments[0].tags
661    @segments.each do |s|
662      if s.tags != @tags
663        raise "all segments in a path must have the same tags"
664      end
665    end
666  end
667
668# }}}
669  def rendercount# {{{
670    2
671  end
672
673  # }}}
674  def render# {{{
675    ps = ""
676    @tags.renderlayers.sort.each do |l|
677      ps += renderlayer(l)
678    end
679    ps
680  end
681
682  # }}}
683  def renderlayer(l)# {{{
684    tr = @tags.renderlayer(l)
685    if tr == ""
686      return ""
687    end
688    ps = "mark #{@segments[0].from.x} #{@segments[0].from.y}\n"
689    @segments.each do |s|
690      ps += "#{s.to.x} #{s.to.y}\n"
691    end
692    ps += tr
693    ps += "cleartomark\n"
694    ps
695  end
696
697  # }}}
698end
699
700# }}}
701class Graph#{{{
702  def initialize# {{{
703    @nodes = {}
704    @segments = {}
705    @tags = {}
706    @groups = {}
707    @paths = {}
708    @styles = []
709  end
710 
711  # }}}
712  def addnode(node)# {{{
713    node.setgraph(self)
714    @nodes[node] = {}
715    # this call will use findtags to make sure that duplicate tags
716    # are stored in the same Tag object
717    setobjtags(node, node.tags)
718  end
719 
720  # }}}
721  def addsegment(segment)# {{{
722    segment.setgraph(self)
723    @segments[segment] = {}
724    # this call will use findtags to make sure that duplicate tags
725    # are stored in the same Tag object
726    setobjtags(segment, segment.tags)
727  end
728 
729  # }}}
730  def addpath(path)# {{{
731    path.setgraph(self)
732    @paths[path] = 1
733  end
734 
735  # }}}
736  def addstyle(style)# {{{
737    @styles.push(style)
738  end
739 
740  # }}}
741  def makepaths# {{{
742    @paths = {}
743    seg = @segments.keys.dup
744
745    while (seg.length > 0)
746      p = Path.new(buildpath(seg))
747      addpath(p)
748    end
749  end
750
751  # }}}
752  def nodes# {{{
753    @nodes.keys
754  end
755 
756  # }}}
757  def segments# {{{
758    @segments.keys
759  end
760 
761  # }}}
762  def tags# {{{
763    @tags
764  end
765 
766  # }}}
767  def addtags(obj, tags)# {{{
768    if tags == nil
769      return
770    end
771    if obj.tags == nil
772      newtags = tags
773    else
774      newtags = obj.tags.tags.merge(tags)
775    end
776    setobjtags(obj, newtags)
777  end
778 
779  # }}}
780  def findtags(tags)# {{{
781    @tags.keys.each do |t|
782      if t.tags == tags
783        return t
784      end
785    end
786    nil
787  end
788 
789  # }}}
790  def setobjtags(obj, tags)# {{{
791    tagdec(obj.tags)
792    t = findtags(tags)
793    if t == nil
794      t = Tags.new(tags)
795    end
796    obj.settags(t)
797    taginc(t)
798  end
799 
800  # }}}
801  def to_s# {{{
802    str = "== nodes ==\n"
803    @nodes.keys.each do |n|
804      str += "#{n} "
805      str += "#{n.tags}\n"
806    end
807
808    str = "== segments ==\n"
809    @segments.keys.each do |s|
810      str += "#{s} "
811      str += "#{s.tags}\n"
812    end
813
814    str += "== tags ==\n"
815    @tags.keys.each do |t|
816      str += "#{t.to_s}\n"
817    end
818    str
819  end
820 
821  # }}}
822
823  def bbox# {{{
824    mm = {}
825    mm[:miny] = mm[:maxy] = @nodes.keys[0].y
826    mm[:minx] = mm[:maxx] = @nodes.keys[0].x
827
828    @nodes.keys.each do |n|
829      if n.y <  mm[:miny]
830        mm[:miny] = n.y
831      end
832      if n.x < mm[:minx]
833        mm[:minx] = n.x
834      end
835      if n.y >  mm[:maxy]
836        mm[:maxy] = n.y
837      end
838      if n.x > mm[:maxx]
839        mm[:maxx] = n.x
840      end
841    end
842
843    mm
844  end
845
846  #}}}
847  def eps(width, height)# {{{
848    ps = "%!PS-Adobe-3.0 EPSF-2.0\n"
849    ps += "%%Creator: osmps.rb\n"
850    ps += "%%BoundingBox: 0 0 #{width} #{height}\n"
851    ps += "%%EndComments\n"
852    mm = bbox()
853    ps += PSResource
854    ps += <<EOP
855/mx #{mm[:minx]} def
856/Mx #{mm[:maxx]} def
857/my #{mm[:miny]} def
858/My #{mm[:maxy]} def
859/width #{width} def
860/height #{height} def
861% scale
862/sc {
863  width Mx mx sub div
864  height My my sub div
865  2 copy gt {exch pop} {pop} ifelse
866} bd
867% x and y translations
868/x { mx sub sc mul } bd
869/y { my sub sc mul height exch sub} bd
870EOP
871
872#    ps += "% nodes\n"
873#    ps += "newpath\n"
874#    @nodes.keys.each do |n|
875#      ps += "#{n.x} x #{n.y} y n\n"
876#    end
877#    ps += "stroke\n"
878
879    ps += "\n"
880    ps += "0.1 setlinewidth\n"
881    ps += "0 setgray\n"
882    ps += "\n"
883    ps += "% paths\n"
884    ps += "1 setlinecap 1 setlinejoin\n"
885
886    layers = {}
887    @tags.keys.each do |tag|
888      tag.renderprepare(@styles)
889      tag.renderlayers.each do |l|
890        layers.has_key?(l) ? layers[l].push(tag) : layers[l] = [tag]
891      end
892    end
893
894    layers.keys.sort.each do |l|
895      @tags.keys.each do |tag|
896        if layers[l].include?(tag)
897          content = ""
898          @paths.keys.each do |p|
899            if p.tags == tag
900              content += p.renderlayer(l)
901            end
902          end
903          if content != ""
904            ps += "% #{tag.to_s}\n"
905            ps += tag.renderpre
906            ps += content
907            ps += tag.renderpost
908          end
909        end
910      end
911    end
912   
913    ps += "%%EOF\n"
914    ps
915  end
916
917 
918
919# }}}
920  private
921
922  def taginc(tags)# {{{
923    if tags == nil
924      return
925    end
926    if @tags.has_key?(tags)
927      @tags[tags] += 1
928    else
929      @tags[tags] = 1
930    end
931  end
932 
933  # }}}
934  def tagdec(tags)# {{{
935    if tags == nil
936      return
937    end
938    if @tags.has_key?(tags)
939      @tags[tags] -= 1
940      if @tags[tags] == 0
941        @tags.delete(tags)
942      end
943    end
944  end
945 
946  # }}}
947  def buildpath(segments)# {{{
948  # this finds a path in the segments by whether adjacent segments
949  # have the same tags or not.
950  # it is slightly broken - need to check for currseg.to OR
951  # currseg.from depending on the node we came from (a path could
952  # consist of segments that point in different directions)
953  # hmm - maybe... ----oneway---><---oneway---- would break this, so
954  # probably it is ok.
955  # It _does_ need to be extended to always find the longest
956  # possible path, otherwise names can occasionally be placed on
957  # roads in unusual places!
958    path = []
959    debug = false
960
961    currseg = segments[0]
962
963    tags = currseg.tags
964    # forward
965    while currseg != nil
966      path.push(currseg)
967      segments.delete(currseg)
968      candidates = []
969      if debug
970        puts "considering F segment #{currseg.to_s}"
971      end
972      currseg.to.segments.each do |s|
973        if debug
974          puts "  looking at segment #{s.to_s}"
975          if s.tags != tags
976            puts "    tags don't match"
977          end
978          if path.include?(s)
979            puts "    path includes this"
980          end
981        end
982        if s.from == currseg.to and s.tags == tags and not path.include?(s)
983          if segments.include?(s)
984            candidates.push(s)
985          end
986        end
987      end
988      currseg = nil
989      if candidates.length > 0
990        currseg = candidates[0]
991      end
992    end
993
994    currseg = path[0]
995    path.shift
996    # backwards
997    while currseg != nil
998      path.unshift(currseg)
999      segments.delete(currseg)
1000      if debug
1001        puts "considering B segment #{currseg.to_s}"
1002      end
1003      candidates = []
1004      currseg.from.segments.each do |s|
1005        if debug
1006          puts "  looking at segment #{s.to_s}"
1007          if s.tags != tags
1008            puts "    tags don't match"
1009          end
1010          if path.include?(s)
1011            puts "    path includes this"
1012          end
1013        end
1014        if s.to == currseg.from and s.tags == tags and not path.include?(s)
1015          if segments.include?(s)
1016            candidates.push(s)
1017          end
1018        end
1019      end
1020      currseg = nil
1021      if candidates.length > 0
1022        currseg = candidates[0]
1023      end
1024    end
1025
1026    path
1027  end
1028
1029  # }}}
1030
1031end# }}}
1032
1033def importosm(file) #{{{
1034  # open xml document
1035  doc = XML::Document.file(file)
1036  root = doc.root
1037
1038  g = Graph.new
1039
1040  # hash to keep track of osm ids
1041  nodes = {}
1042  # read nodes
1043  doc.find("//osm/node").each do |n|
1044    tags = {}
1045    n.each_child do |c|
1046      if c.name == "tag"
1047        tags[c['k']] = c['v']
1048      end
1049    end
1050    node = Node.new(n['lon'], n['lat'])
1051    node.settags(tags)
1052    node.setosmid(n['id'])
1053    g.addnode(node)
1054    nodes[n['id']] = node
1055  end
1056
1057  # hash for osm segment ids
1058  segments = {}
1059  # read segments
1060  doc.find("//osm/segment").each do |s|
1061    tags = {}
1062    s.each_child do |c|
1063      if c.name == "tag" and c['k'] != "created_by"
1064        tags[c['k']] = c['v']
1065      end
1066    end
1067    segment = Segment.new(nodes[s['from']], nodes[s['to']])
1068    segment.settags(tags)
1069    segment.setosmid(s['id'])
1070    g.addsegment(segment)
1071    segments[s['id']] = segment
1072  end
1073
1074  # pull in all the ways. we don't care about them per se, but
1075  # we do care that the segments in a way get the way's tags
1076  doc.find("//osm/way").each do |w|
1077    tags = {}
1078    segs = {}
1079    w.each_child do |c|
1080      if c.name == "tag" and c['k'] != "created_by"
1081        tags[c['k']] = c['v']
1082      end
1083      if c.name == "seg"
1084        # add segment to segs list only if it existed in the osm file
1085        if segments.has_key?(c['id'])
1086          segs[segments[c['id']]] = 1
1087        end
1088      end
1089    end
1090    segs.keys.each do |s|
1091      s.addtags(tags)
1092    end
1093  end
1094
1095  g
1096end #}}}
1097
1098if not FileTest.exist?(ARGV[0].to_s)
1099  puts "Syntax: #{$0} <filename.osm>"
1100  puts "PostScript it output on STDOUT"
1101  exit
1102end
1103
1104g = importosm(ARGV[0])
1105
1106# {{{ styles
1107#
1108#
1109# layer numbers
1110#
1111# 0    - 999     Land use / areas below everything else
1112# 1000 - 1999    Water features
1113# 2000 - 2999    Highway
1114# 3000 - 3999    Railway
1115# 4000 - 4999    Highway bridges
1116# 5000 - 5999    Railway bridges
1117#
1118#
1119#
1120# Land areas 0-999
1121#
1122
1123  s = Style.new()
1124  s.addtag("landuse", nil)
1125  s.setarea
1126  s.adddrawps(0, "220 220 240 c pathcopy area")  # fill in the area
1127  g.addstyle(s)
1128
1129
1130  s = Style.new()
1131  s.addtag("landuse", "residential")
1132  s.setarea
1133  s.adddrawps(10, "220 220 220 c pathcopy area")
1134  g.addstyle(s)
1135
1136  s = Style.new()
1137  s.addtag("landuse", "retail")
1138  s.setarea
1139  s.adddrawps(10, "255 220 220 c pathcopy area")
1140  g.addstyle(s)
1141
1142  s = Style.new()
1143  s.addtag("amenity", "university")
1144  s.setarea
1145  s.adddrawps(10, "225 180 255 c pathcopy area")
1146  g.addstyle(s)
1147
1148
1149  s = Style.new()
1150  s.addtag("leisure", "park")
1151  s.setarea
1152  s.adddrawps(15, "155 215 70 c pathcopy area")
1153  g.addstyle(s)
1154
1155  s = Style.new()
1156  s.addtag("amenity", "parking")
1157  s.setarea
1158  s.adddrawps(20, "220 220 100 c pathcopy area")
1159  g.addstyle(s)
1160
1161  s = Style.new()
1162  s.addtag("landuse", "green_space")
1163  s.setarea
1164  s.adddrawps(20, "220 255 220 c pathcopy area")
1165  g.addstyle(s)
1166
1167  s = Style.new()
1168  s.addtag("leisure", "pitch")
1169  s.setarea
1170  s.adddrawps(20, "30 160 30 c pathcopy area")
1171  g.addstyle(s)
1172
1173  s = Style.new()
1174  s.addtag("landuse", "wood")
1175  s.addtag("natural", "wood")
1176  s.setarea
1177  s.adddrawps(20, "10 70 10 c pathcopy area")
1178  g.addstyle(s)
1179
1180
1181# Water 1000-1999
1182
1183  s = Style.new()
1184  s.addtag("waterway", "river")
1185  s.setpath
1186  s.adddrawps(1100, "1.8 lw 120 120 220 c pathcopy line")
1187  g.addstyle(s)
1188
1189  s = Style.new()
1190  s.addtag("waterway", "canal")
1191  s.setpath
1192  s.adddrawps(1200, "0.7 lw 70 70 220 c pathcopy line")
1193  g.addstyle(s)
1194
1195  s = Style.new()
1196  s.addtag("waterway", "stream")
1197  s.setpath
1198  s.adddrawps(1300, "0.7 lw 120 120 220 c pathcopy line")
1199  g.addstyle(s)
1200
1201  s = Style.new()
1202  s.addtag("natural", "water")
1203  s.setarea
1204  s.adddrawps(1400, "120 120 220 c pathcopy area")
1205  s.adddrawps(1401, "0.1 lw 80 80 220 c pathcopy cline")
1206  g.addstyle(s)
1207
1208# Highway 2000-2999
1209
1210def setroad(sty, l1, l2, width, bridge, casecol, corecol)
1211  brcasew = width * 1.6
1212  brcorew = width * 1.4
1213  corew = width * 0.7
1214  sty.setpath
1215  if bridge
1216    sty.addtag("bridge", "yes")
1217    sty.adddrawps(4000, "0 setlinecap #{brcasew} lw 0 0 0 c pathcopy line")
1218    sty.adddrawps(4001, "0 setlinecap #{brcorew} lw 255 255 255 c pathcopy line")
1219    if l1 != nil
1220      sty.adddrawps(l1 + 2000, "0 setlinecap #{width} lw #{casecol} c pathcopy line") # casing over bridge
1221    end
1222    sty.adddrawps(l2 + 2100, "0 setlinecap #{corew} lw #{corecol} c pathcopy line") # core
1223  else 
1224    if l1 != nil
1225      sty.adddrawps(l1, "#{width} lw #{casecol} c pathcopy line")  # casing
1226    end
1227    sty.adddrawps(l2, "#{corew} lw #{corecol} c pathcopy line pathcopy")  # core
1228    sty.adddrawps(2999, "pathcopy")  # name
1229    sty.adddrawtagstring(2999, "name")
1230    sty.adddrawps(2999, "roadname")
1231  end
1232end
1233
1234for bridge in [false, true]
1235  bl = bridge ? 2000 : 0
1236  s = Style.new()
1237  s.addtag("highway", "motorway")
1238  setroad(s, 2050, 2500, 1, bridge, "0 0 0", "20 60 200")
1239  g.addstyle(s)
1240
1241  s = Style.new()
1242  s.addtag("highway", "trunk")
1243  setroad(s, 2050, 2580, 1, bridge, "0 0 0", "50 150 50")
1244  g.addstyle(s)
1245
1246  s = Style.new()
1247  s.addtag("highway", "primary")
1248  setroad(s, 2050, 2560, 1, bridge, "0 0 0", "255 50 50")
1249  g.addstyle(s)
1250
1251  s = Style.new()
1252  s.addtag("highway", "secondary")
1253  setroad(s, 2050, 2540, 0.9, bridge, "0 0 0", "250 75 10")
1254  g.addstyle(s)
1255
1256  s = Style.new()
1257  s.addtag("highway", "tertiary")
1258  setroad(s, 2050, 2520, 0.8, bridge, "0 0 0", "220 220 0")
1259  g.addstyle(s)
1260
1261  s = Style.new()
1262  s.addtag("highway", ["unclassified", "residential"])
1263  setroad(s, 2050, 2500, 0.8, bridge, "0 0 0", "240 240 240")
1264  g.addstyle(s)
1265
1266  s = Style.new()
1267  s.addtag("highway", "motorway_link")
1268  setroad(s, 2050, 2480, 0.7, bridge, "0 0 0", "20 60 200")
1269  g.addstyle(s)
1270
1271  s = Style.new()
1272  s.addtag("highway", "trunk_link")
1273  setroad(s, 2050, 2460, 0.7, bridge, "0 0 0", "50 150 50")
1274  g.addstyle(s)
1275
1276  s = Style.new()
1277  s.addtag("highway", "primary_link")
1278  setroad(s, 2050, 2440, 0.7, bridge, "0 0 0", "255 50 50")
1279  g.addstyle(s)
1280
1281  s = Style.new()
1282  s.addtag("highway", "secondary_link")
1283  setroad(s, 2050, 2420, 0.5, bridge, "0 0 0", "250 75 10")
1284  g.addstyle(s)
1285
1286  s = Style.new()
1287  s.addtag("highway", "service")
1288  setroad(s, 2050, 2400, 0.5, bridge, "0 0 0", "250 250 250")
1289  g.addstyle(s)
1290
1291  s = Style.new()
1292  s.addtag("highway", "track")
1293  setroad(s, 2030, 2380, 0.5, bridge, "80 30 0", "250 250 250")
1294  g.addstyle(s)
1295
1296  s = Style.new()
1297  s.addtag("highway", ["footway", "steps"])
1298  setroad(s, nil, 2140, 0.2, bridge, nil, "80 30 0")
1299  g.addstyle(s)
1300
1301  s = Style.new()
1302  s.addtag("highway", "cycleway")
1303  setroad(s, nil, 2160, 0.3, bridge, nil, "0 80 0")
1304  g.addstyle(s)
1305end
1306
1307# Rail 3000 - 3999
1308
1309  s = Style.new()
1310  s.addtag("railway", "rail")
1311  s.setpath
1312  s.adddrawps(3500, "1 lw 0 0 0 c pathcopy line")  # core
1313  g.addstyle(s)
1314
1315  s = Style.new()
1316  s.addtag("railway", "preserved")
1317  s.setpath
1318  s.adddrawps(3300, "0.6 lw 25 25 25 c pathcopy line")  # core
1319  g.addstyle(s)
1320
1321# }}}
1322
1323g.makepaths
1324puts g.eps(500,500)
1325
Note: See TracBrowser for help on using the repository browser.