source: subversion/applications/rendering/makeMapOnMove/mm.pl @ 30554

Last change on this file since 30554 was 2530, checked in by jochen, 12 years ago
  • moving things around
File size: 10.0 KB
Line 
1#!/usr/bin/perl
2use strict;
3#---------------------------------------------------------------------------------
4# Make map on move
5#
6# Usage: mm.pl
7#  and then open index.htm in a browser where you can see it with the console still open
8#  then reselect the console window so that it receives keystrokes
9#
10#  All input should be to console window via the numeric keypad
11#  All output will be to the browser
12#  Press q to quit
13#---------------------------------------------------------------------------------
14# Copyright 2007, Oliver White
15#
16# This program is free software; you can redistribute it and/or modify
17# it under the terms of the GNU General Public License as published by
18# the Free Software Foundation; either version 2 of the License, or
19# (at your option) any later version.
20#
21# This program is distributed in the hope that it will be useful,
22# but WITHOUT ANY WARRANTY; without even the implied warranty of
23# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24# GNU General Public License for more details.
25#
26# You should have received a copy of the GNU General Public License
27# along with this program; if not, write to the Free Software
28# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
29#---------------------------------------------------------------------------------
30
31# We only use this for non-blocking getch()
32require Term::Screen;
33my $scr = new Term::Screen;
34die("Can't init term::screen\n") if(!$scr);
35$scr->clrscr();
36
37# Setup stuff for a fake GPS (that reads a logfile)
38use Time::HiRes qw(sleep);
39open(my $fpLogIn, "<log.nmea") || die("Can't open log");
40
41# Global variables
42my ($CurrentWay, @Poi, @Ways);
43my ($posLat,$posLon) = (0,0);
44my $Mode = "default";
45my ($LastKey, $LastError);
46my @Interface;
47
48# Interface commands
49loadInterface("interface.txt");
50
51startNewWay("unclassified");
52
53# Run (100 steps only in testing)
54foreach(1..100){
55  update();
56  render();
57  sleep(0.2);
58}
59
60sub startNewWay{
61  $CurrentWay = {};
62  $CurrentWay->{"tags"} = {"highway"=>shift()}; # default tags
63  $CurrentWay->{"nodes"} = [];
64}
65
66# Updates everything
67sub update{
68  # Get the position
69  my ($lat,$lon,$valid) = getPos();
70  if($valid){
71    # Record the position, add it to our journey
72    addNode($lat,$lon);
73    $posLat = $lat; 
74    $posLon = $lon;
75  }
76  processInput();
77  showHtml();
78  outputOsm();
79}
80
81sub showHtml{
82  # Create an HTML page as output
83 
84  # Take input from a template HTML file
85  open(my $fp, "<template.htm") || die("Can't read HTML template\n");
86  my $template = join("", <$fp>);
87  close $fp;
88 
89  # List of keywords to look for
90  foreach my $Keyword( qw(KEYPAD POS MODE ATTRIBUTES LAST_KEYPRESS LAST_ERROR)){
91    # Lookup each keyword in the getHtmlPart function
92    my $Data = getHtmlPart($Keyword);
93    $template =~ s/\{\{$Keyword\}\}/$Data/g;
94  }
95 
96  # Save the output to index.htm
97  open(my $fp, ">index.htm") || die("Can't write HTML\n");
98  print $fp $template;
99  close $fp;
100}
101
102sub getHtmlPart{
103  my $Key = shift();
104  # HTML keywords that can be replaced
105  return getHtmlKeypad() if($Key eq "KEYPAD");
106  return sprintf("%1.5f, %1.5f", $posLat,$posLon) if($Key eq "POS");
107  return $Mode if($Key eq "MODE");
108  return getHtmlAttributes() if($Key eq "ATTRIBUTES");
109  return $LastKey if($Key eq "LAST_KEYPRESS");
110  return $LastError if($Key eq "LAST_ERROR");
111}
112sub getHtmlKeypad{
113  # Get the currently-available interface, as an HTML table
114  my %Keys;
115 
116  # Look through the interface definition for keypresses which exist in this mode
117  foreach my $Interface(@Interface){
118    my($ifMode,$ifKey,$ifLabel,$ifAction,$ifParams) = split(/:/,$Interface);
119    if($ifMode eq $Mode){
120      $Keys{$ifKey} = $ifLabel;
121    }
122  }
123 
124  # Create an HTML table representing our input device
125  my $Keypad = 
126    "<table class=\"keypad\" cellspacing=\"0\"><tr><td>{7}</td><td>{8}</td><td>{9}</td></tr>\n".
127    "<tr><td>{4}</td><td>{5}</td><td>{6}</td></tr>\n".
128    "<tr><td>{1}</td><td>{2}</td><td>{3}</td></tr>\n".
129    "<tr><td>{0}</td><td>{.}</td><td>{Enter}</td></tr></table>\n";
130   
131  # Replace every {{key}} entry with a label from the interface definition
132  while(my ($k,$v) = each(%Keys)){
133    my $htmlVal = "<span class=\"keyname\">$k</span><br><span class=\"keyaction\">$v</span>";
134    $Keypad =~ s/\{$k\}/$htmlVal/g;
135  }
136 
137  # Replace any unused key entries with HTML spaces
138  $Keypad =~ s/\{.*?\}/&nbsp;/g;
139 
140  return $Keypad;
141}
142
143sub getHtmlAttributes{
144  # Get the current way's attributes, as an HTML table
145  my $Html = "<table class=\"attributes\">";
146  my $TagRef = $CurrentWay->{"tags"};
147  while(my($k,$v) = each(%$TagRef)){
148    $Html .= "<tr><td>$k</td><td>$v</td></tr>\n";
149  }
150  return($Html ."</table>");
151}
152sub processInput{
153  # Handle any keyboard input
154  return if(!$scr->key_pressed());
155  my $c = $scr->getch();
156  finish() if($c eq "q");
157  $LastKey = $c;
158 
159  # Look for an interface command that matches what was just pressed
160  # and which is valid in the current mode, and implement it
161  foreach my $Interface(@Interface){
162    my($ifMode,$ifKey,$ifLabel,$ifAction,$ifParams) = split(/:/,$Interface);
163    if($ifMode eq $Mode and $ifKey eq $c){
164      handleInputEvent($ifAction, $ifParams);
165      return;
166    }
167  }
168  $Mode = "default";
169  return;
170 
171}
172sub handleInputEvent{
173  # Do something as the result of a keypress
174  my ($action, $params) = @_;
175  if($action eq "mode"){
176    $Mode = $params;
177  }
178  elsif($action eq "set"){
179    setTags(split(/=/,$params));
180    $Mode = "default";
181  }
182  elsif($action eq "add"){
183    addPoi($params);
184  }
185  elsif($action eq "action"){
186  doAction($params);
187  }
188  else{
189    $LastError = "Unrecognised input event $action";
190  }
191}
192sub doAction{
193  my $action = shift();
194  if($action eq "split_way"){
195    splitWay();
196  }
197  else{
198    $LastError = "Unrecognised action $action";
199  }
200}
201sub splitWay{
202  my $OldHighway = $CurrentWay->{"tags"}->{"highway"};
203  push(@Ways, $CurrentWay);
204  startNewWay($OldHighway);
205}
206sub addPoi{
207  # Add a node as POI (not in route)
208  my $KeyVal = shift();
209  my $PoiData;
210 
211  foreach my $Part(split(/,\s*/,$KeyVal)){
212    my($k,$v) = split(/=/,$Part);
213    $PoiData->{$k} = $v;
214    }
215  $PoiData->{"lat"} = $posLat;
216  $PoiData->{"lon"} = $posLon;
217 
218  push(@Poi, $PoiData);
219}
220sub setTags{
221  # Set attributes of the current Way
222  my ($k,$v) = @_;
223  $CurrentWay->{"tags"}->{$k} = $v;
224}
225
226sub addNode{
227  # Add a node to the current Way
228  my $Node;
229  $Node->{lat} = shift();
230  $Node->{lon} = shift();
231  my $Ref = $CurrentWay->{"nodes"};
232  push @$Ref, $Node;
233}
234sub outputOsmWay{
235  my ($Way, 
236    $NodeCountRef, $SegmentCountRef, $WayCountRef, 
237    $NodeXmlRef, $SegmentXmlRef, $WayXmlRef) = @_;
238 
239  my $NodesRef = $Way->{"nodes"};
240  my $TagsRef = $Way->{"tags"};
241 
242  my $WaySegmentData = "";
243 
244  my $LocalCount = 0;
245 
246  foreach my $Node (@$NodesRef){
247    $$NodeXmlRef .= sprintf("<node id='%d' lat='%f' lon='%f' />\n",
248      ++$$NodeCountRef,
249      $Node->{lat},
250      $Node->{lon});
251     
252    if($LocalCount > 0){
253      $$SegmentXmlRef .= sprintf("<segment id='%d' from='%d' to='%d' />\n",
254        ++$$SegmentCountRef,
255        $$NodeCountRef-1,
256        $$NodeCountRef);
257     
258      $WaySegmentData .= sprintf("<seg id='%d' />\n",
259        $$SegmentCountRef);
260    }
261   
262    $LocalCount++;
263  }
264 
265  my $TagData = "";
266  while(my ($k,$v) = each(%$TagsRef)){
267     $TagData .= sprintf("<tag k='%s' v='%s' />\n",$k,$v);
268   }
269
270  $$WayXmlRef .= sprintf("<way id='%d'>\n%s%s\n</way>\n", 
271    ++$$WayCountRef,
272    $WaySegmentData,
273    $TagData);
274 
275}
276sub outputOsm{
277  # Save everything we know as an OSM file
278  open(my $fp, ">data.osm") || die("Can't write to OSM file\n");
279 
280  print $fp "<?xml version='1.0' encoding='UTF-8'?>\n";
281  print $fp "<osm version='0.3' generator='mm'>\n";
282
283  my($NodeID, $SegmentID, $WayID) = (0,0,0);
284  my($NodeXML,$SegmentXML,$WayXML) = ("","","");
285
286
287  outputOsmWay(
288    $CurrentWay, 
289    \$NodeID, \$SegmentID, \$WayID, 
290    \$NodeXML, \$SegmentXML, \$WayXML);
291 
292  foreach my $Way(@Ways){
293    outputOsmWay(
294      $Way, 
295      \$NodeID, \$SegmentID, \$WayID, 
296      \$NodeXML, \$SegmentXML, \$WayXML);
297  }
298
299 
300  # Save POI nodes to OSM file
301  foreach my $Poi (@Poi){
302    $NodeXML .= sprintf(
303      "<node id='%d' lat='%f' lon='%f'>\n", 
304      ++$NodeID, 
305      $Poi->{"lat"},
306      $Poi->{"lon"});
307     
308    while(my($k,$v) = each(%$Poi)){
309      $NodeXML .= sprintf("<tag k='%s' v='%s' />\n",$k,$v) if($k !~ /^(lat|lon)$/);
310    }
311    $NodeXML .= "</node>\n",
312  }
313 
314  # Save current position as a node
315  $NodeXML .= sprintf("<node id='%d' lat='%f' lon='%f'>\n", ++$NodeID, $posLat, $posLon);
316  $NodeXML .= "<tag k='mapmaker' v='current_position' />\n";
317  $NodeXML .= "</node>\n",
318 
319  # Save bounding box, makes osmarender give a map centred on the current position
320  my $HalfAreaLat = 0.003;
321  my $HalfAreaLon = $HalfAreaLat * (cos($posLon / 57));
322  printf $fp "<bounds returned_minlat=\"%f\" returned_minlon=\"%f\" returned_maxlat=\"%f\" returned_maxlon=\"%f\" />\n",
323    $posLat - $HalfAreaLat,
324    $posLon - $HalfAreaLon,
325    $posLat + $HalfAreaLat,
326    $posLon + $HalfAreaLon;
327 
328  print $fp $NodeXML;
329  print $fp $SegmentXML;
330  print $fp $WayXML;
331 
332  print $fp "</osm>";
333  close $fp;
334}
335
336sub render(){
337  # Render the OSM data we just created
338  `cd render;./render.sh 2>/dev/null &`;
339}
340
341sub getPos{
342  # Get current position as WGS-84 lat/long
343  # This should be replaced with a GPSD call later
344  my $Line = <$fpLogIn>;
345  chomp $Line;
346  if($Line =~ /\$GPGGA,(.*)/){
347    my ($Time,$Lat,$NS,$Long,$WE,@others) = split(/,/,$1);
348   
349    my $Lat = convertPos($Lat,$NS);
350    my $Long = convertPos($Long,$WE);
351   
352    printf "%1.13f, %1.13f\n", $Lat, $Long if(0);
353
354    return($Lat,$Long,1);
355  }
356  return(0,0,0);
357}
358
359
360sub convertPos{
361  # Convert NMEA-style number format (DDMM.MMM) to decimal
362  my ($Num,$Quadrant) = @_;
363  if($Num =~ /(\d+)(\d{2}\.\d+)/){
364    $Num = $1 + $2 / 60;
365  }
366  if($Quadrant =~ /[SW]/){
367    $Num *= -1;
368  }
369  return($Num);
370}
371sub finish{
372  # Exit the program
373  $scr->clrscr();
374  exit;
375}
376
377sub loadInterface{
378  # Load interface definition file from disk
379  open(my $fp, "<", shift()) || die("Can't read interface definition\n");
380  while(my $Line = <$fp>){
381    chomp $Line;
382    $Line =~ s/\s*#.*$//;  # Remove comments
383    push @Interface, $Line;
384  }
385  close $fp;
386}
Note: See TracBrowser for help on using the repository browser.