credit silk (famfamfam)
[maps-routes-comparison] / index.php
1 <?php
2
3 // Copyright 2010 Guillaume Cottenceau.
4 // 
5 // Licensed under the Apache License, Version 2.0 (the "License"); you
6 // may not use this site or the software running this site except in
7 // compliance with the License. You may obtain a copy of the License at
8 // 
9 //            http://www.apache.org/licenses/LICENSE-2.0
10 // 
11 // Unless required by applicable law or agreed to in writing, this is
12 // provided on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
13 // KIND, either express or implied. See the License for the specific
14 // language governing permissions and limitations under the
15 // License.
16
17 ini_set("track_errors", 1);
18 ini_set('display_errors', 0);
19
20 include("i18n.phpi");
21
22 // Helper function for fatal errors
23 function fatal($msg) {
24     echo "<h2>Error</h2>\n";
25     echo "$msg\n";
26     echo "</body></html>";
27     exit;
28
29 }
30
31 error_log($_SERVER['REQUEST_URI']);
32
33 if (!preg_match('/saved/', $_SERVER['REQUEST_URI'])) {
34     $host = 'api.hostip.info';
35     $port = 80;
36     $path = '/get_html.php?position=true&ip=' . $_SERVER['REMOTE_ADDR'];
37     
38     error_log('Geolocate with api.hostip.info...');
39     $fp = @fsockopen($host, $port, $errno, $errstr);
40     if (!$fp)
41         fatal("<p>Unable to open connection to <tt>$host</tt> ".
42               "(port <tt>$port</tt>):</p><pre>$errstr</pre>");
43     
44     fwrite($fp, "GET $path HTTP/1.0\r\n");
45     fwrite($fp, "Host: $host:$port\r\n");
46     fwrite($fp, "\r\n");
47     
48     $buf = '';
49     $length = '';
50     while (!feof($fp)) {
51         $buf .= fread($fp, 8192);
52         // Parse the HTTP answer and try to extract response code,
53         // headers and body
54         $answer = parse_answer($buf);
55     
56         // We need to discover Content-Length, to know when server
57         // finishes sending data
58         if (!$length) {
59             if ($answer[0] == 200)
60                 $length = $answer[1]['content-length'];
61         }
62         
63         // When length is available, we can check if response has
64         // been fully transmitted; this is the case when all what
65         // we received equals the headers plus the full contents.
66         if ($length) {
67             if (strlen_b($buf) == strlen_b($answer[3]) + $length)
68                 break;
69         }
70     }
71     
72     fclose($fp);
73     $body = $answer[2];
74     
75     $latitude = $longitude = $zoom = 0;
76     if (preg_match('/Latitude: (\S+)/', $body, $matches)) {
77         error_log('Got lat/lng information from api.hostip.info');
78         $latitude = $matches[1];
79         $zoom = 8;
80     }
81     if (preg_match('/Longitude: (\S+)/', $body, $matches)) {
82         $longitude = $matches[1];
83     }
84     if (($latitude == 0 || $longitude == 0) && preg_match('/Country: .*?\((\S{2})\)/', $body, $matches)) {
85         $country = $matches[1];
86         if ($country == 'XX') {
87             // no luck with hostip.info, at least position to proper country with whois on IP
88             error_log('Geolocate with whois...');
89             $whois = shell_exec("whois " . $_SERVER['REMOTE_ADDR']);
90             if (preg_match('/country:\s*(\S{2})/i', $whois, $matches)) {
91                 error_log('Got country information from whois');
92                 $country = $matches[1];
93             }
94         } else {
95             error_log('Got only country information from api.hostip.info');
96         }
97         include('countries.phpi');
98         $latitude = $countries[$country][0];
99         $longitude = $countries[$country][1];
100         $zoom = 5;
101     }
102 }
103
104 // PHP's strlen should return the length of a string in bytes,
105 // except when using multibyte strings, in which case
106 // mb_orig_strlen must be used (in case strlen was overloaded
107 // with mb_strlen).
108 function strlen_b($str) {
109     if (function_exists('mb_orig_strlen')) {
110         return mb_orig_strlen($str);
111     } else {
112         return strlen($str);
113     }
114 }
115
116 // Parse an HTTP answer to extract response code, headers and body
117 // Returns an array containing: response code, headers, body, raw headers
118 function parse_answer($answer) {
119
120     // This first pattern matching allows to separate first line
121     // (server response status), headers, and data contents 
122     if (ereg("^(([^\n]+)\r\n(.*)\r\n\r\n)(.*)", $answer, $regs)) {
123         $full_headers   = $regs[1];
124         $response       = $regs[2];
125         $headers_string = $regs[3];
126         $body           = $regs[4];
127         // Parse first line (server response status)
128         if (ereg("^HTTP/[0-9\.]+ ([0-9]+)", $response, $regs)) {
129             $response_code = $regs[1];
130         } else {
131             fatal("<p>Unable to parse response line <tt>$response</tt> ".
132                   "from the server.</p>");
133         }
134         // Parse headers and build a hash with them
135         foreach (split("\r\n", $headers_string) as $line) {
136             if (ereg("^([^:]+): (.*)", $line, $regs)) {
137                 $headers[strtolower($regs[1])] = $regs[2];
138             } else {
139                 fatal("<p>Unable to parse header line <tt>$line</tt> ".
140                       "from the server. ".
141                       "All headers:</p><pre>$headers_string</pre>");
142             }
143         }
144     } else {
145         // Return -1 as response code if parsing was not possible
146         return array(-1);
147     }
148     return array($response_code, $headers, $body, $full_headers);
149 }
150
151 ?>
152
153 <html>
154 <head>
155     <title>Routes Compare</title>
156     <link rel="stylesheet" type="text/css" media="screen" href="style.css"/>
157     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
158     <script type="text/javascript" src="prototype.js"></script>
159     <script type="text/javascript" src="effects.js"></script>
160     <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false&language=<?php echo $language ?>"></script>
161     <script type="text/javascript" src="googlemaps.js.php"></script>
162     <script type="text/javascript">
163
164       // Copyright (c) Guillaume Cottenceau and licensed under the Apache 2.0 License
165
166       var mylat, mylng;
167       var map;
168       var me;
169       var places = new Array();
170       var geocoder = new google.maps.Geocoder();
171       var directions_service = new google.maps.DirectionsService();
172       var info_window = null;
173       var bounds;
174       var travelmode = google.maps.DirectionsTravelMode.DRIVING;
175       var undo_history = new Array();
176
177       function removePlaces() {
178           for (var i = 0; i < places.length; i++) {
179               places[i].marker.setMap(null);
180               places[i].directions_display.setMap(null);
181           }
182           places = new Array();
183           document.getElementById("resultslist").innerHTML = '';
184       }
185
186       function reload(new_places) {
187           removePlaces();
188           for (var i = 0; i < new_places.length; i++) {
189               createPlace(new_places[i].latlng, new_places[i].name);
190           }
191       }
192
193       function setTravelmode(tm) {
194           if (tm == 'DRIVING') {
195               travelmode = google.maps.DirectionsTravelMode.DRIVING;
196           } else if (tm == 'WALKING') {
197               travelmode = google.maps.DirectionsTravelMode.WALKING;
198           }
199       }
200       function travelmodeChanged() {
201           setTravelmode(document.getElementById("travelmode").value);
202           reload(places);
203           historyPush();
204       }
205
206       function showDirectionCapture(i) {
207           var request = { origin: new google.maps.LatLng(mylat, mylng),
208                           destination: places[i].latlng,
209                           travelMode: travelmode };
210           directions_service.route(request, function(result, status) {
211               if (status == google.maps.DirectionsStatus.OK) {
212                   places[i].directions_display.setMap(map);
213                   places[i].directions_display.setDirections(result);
214                   displayDirectionsMoreinfo(result.routes[0], document.getElementById("directions" + ( 1 + i )));
215               }
216           });
217       }
218       
219       function fitmap() {
220           if (document.getElementById("fitmap").checked && bounds) {
221               map.fitBounds(bounds);
222           }
223       }
224
225       function fitmapChanged() {
226           fitmap();
227           historyPush();
228       }
229
230       function showDirections() {
231           if (places.length > 0) {
232               bounds = new google.maps.LatLngBounds();
233               bounds.extend(new google.maps.LatLng(mylat, mylng));
234           }
235           for (var i = 0; i < places.length; i++) {
236               if (!places[i].directions_display.getMap()) {
237                   showDirectionCapture(i);
238               }
239               bounds.extend(places[i].latlng);
240           }
241           fitmap();
242       }
243
244       function showDel(number) {
245           document.getElementById("delete" + number).style.visibility = 'visible';
246       }
247
248       function hideDel(number) {
249           document.getElementById("delete" + number).style.visibility = 'hidden';
250       }
251
252       function del(number) {
253           var new_places = new Array();
254           for (var i = 0; i < places.length; i++) {
255               if (i == number - 1) {
256                   places[i].marker.setMap(null);
257                   places[i].directions_display.setMap(null);
258               } else {
259                   new_places.push(places[i]);
260               }
261           }
262           reload(new_places);
263           historyPush();
264       }
265
266       function createPlace(latlng, name) {
267           var number = places.length + 1;
268           if (number > 9) {
269               return;
270           }
271           var iconname = 'icon' + ( 7 + number );
272           var marker = new google.maps.Marker({ position: latlng,
273                                                 title:name,
274                                                 icon:getIcon(iconname),
275                                                 shadow:getShadow(iconname) });
276           marker.setMap(map);
277           document.getElementById("resultslist").innerHTML += "<li onmouseover='showDel(" + number + ")' onmouseout='hideDel(" + number + ")'><img class='iconwithtext' src='images/" + iconname + ".png'/> " + name
278                                                               + "<div style='float:right; padding:3; visibility:hidden' id='delete" + number
279                                                               + "'><a href='#' onclick='del(" + number + ")'><img src='images/bin.png'/></a></div>"
280                                                               + "<br/><span id='directions" + number + "'></span></li>";
281           var directions_display = new google.maps.DirectionsRenderer({ preserveViewport: true, suppressMarkers: true, draggable: true });
282           google.maps.event.addListener(directions_display, "directions_changed", function() {
283               displayDirectionsMoreinfo(directions_display.getDirections().routes[0], document.getElementById("directions" + number ));
284           });
285           places.push({marker:marker, latlng:latlng, name:name, directions_display:directions_display});
286           showDirections();
287       }
288
289       function submit() {
290           if (info_window) {
291               info_window.close();
292               info_window = null;
293           }
294           new Effect.Fade(document.getElementById('newbie'), {duration: 0.4});
295           new Effect.Fade(document.getElementById('usecases'), {duration: 0.4});
296           var address = document.getElementById('address').value;
297           geocoder.geocode({ 'address': address },
298                            function(results, status) {
299                                if (status == google.maps.GeocoderStatus.OK) {
300                                    createPlace(results[0].geometry.location, address);
301                                    document.getElementById('address').value = '';
302                                    document.getElementById('address').focus();
303                                    historyPush();
304                                } else {
305                                    document.getElementById('submitshortresult').innerHTML = "<?php echo t('address.not.found') ?>";
306                                    document.getElementById('submitshortresult').style.display = '';
307                                    setTimeout('new Effect.Fade(document.getElementById("submitshortresult"), {duration: 0.4})', 1000);
308                                }
309                            });
310       }
311
312       function getstate() {
313           var link = "me=" + mylat + ";" + mylng;
314           link += "&tm=" + document.getElementById("travelmode").value;
315           link += "&fb=" + document.getElementById("fitmap").checked;
316           link += "&c=" + map.getCenter().lat() + ";" + map.getCenter().lng() + ";" + map.getZoom()
317           for (var i = 0; i < places.length; i++) {
318               link += "&p=" + places[i].latlng.lat() + ";" + places[i].latlng.lng() + ";" + encodeURI(places[i].name);
319           }
320           return link;
321       }
322
323       function save() {
324           document.getElementById('savediv').style.display = '';
325           var save = document.getElementById('save');
326           save.value = 'http://routes-compare.zarb.org/?saved#' + getstate();
327           save.focus();
328           save.select();
329       }
330
331       function startUp(setcenter) {
332           if (me) {
333               me.setMap(null);
334           }
335           me = new google.maps.Marker({position: new google.maps.LatLng(mylat, mylng),
336                                        title: 'Me',
337                                        draggable: true,
338                                        icon: getIcon('icon31'),
339                                        shadow: getShadow('icon31')});
340           me.setMap(map);
341           google.maps.event.addListener(me, "dragend", function(mouseEvent) {
342                   mylat = mouseEvent.latLng.lat();
343                   mylng = mouseEvent.latLng.lng();
344                   reload(places);
345                   historyPush();
346               });
347           if (setcenter) {
348               map.setCenter(new google.maps.LatLng(mylat, mylng));
349           }
350           return me;
351       }
352
353       function setFromState(state) {
354           var params = state.split("&");
355           for (var i = 0; i < params.length; i++) {
356               var keyvalue = params[i].split("=");
357               var values = keyvalue[1].split(";");
358               if (keyvalue[0] == 'me') {
359                   mylat = values[0];
360                   mylng = values[1];
361               } else if (keyvalue[0] == 'tm') {
362                   setTravelmode(values[0]);
363                   var tm = document.getElementById("travelmode");
364                   for (var j = 0; j < tm.length; j++) {
365                       if (tm.options[j].value == values[0]) {
366                         tm.options[j].selected = true;
367                       }
368                   }
369               } else if (keyvalue[0] == 'fb') {
370                   document.getElementById("fitmap").checked = values[0] == 'true';
371               } else if (keyvalue[0] == 'c') {
372                   map.setCenter(new google.maps.LatLng(values[0], values[1]));
373                   map.setZoom(parseInt(values[2]));
374               } else if (keyvalue[0] == 'p') {
375                   createPlace(new google.maps.LatLng(values[0], values[1]), decodeURI(values[2]));
376               }
377           }
378           startUp(false);
379       }
380
381       function historyPush() {
382           var state = getstate();
383           if (undo_history.length == 0 || undo_history[undo_history.length-1] != state) {
384               undo_history.push(getstate());
385               if (undo_history.length == 2) {
386                   document.getElementById('undo').style.opacity = 1.0;
387                   document.getElementById('undo').style.filter = '';
388               }
389               //              debug('after push: ' + undo_history);
390           } else {
391             //            debug('not pushing ' + state);
392           }
393       }
394       function historyPop() {
395           // when loading a map-related state, a new event is fired and a history element is thus added;
396           // to compensate for that effect, two pops are performed here, and an additional push below to
397           // have the same effect for non map-related states
398           undo_history.pop();
399           var state = undo_history.pop();
400           //          debug('state to load: ' + state);
401           if (state) {
402               removePlaces();
403               setFromState(state);
404               historyPush();  
405               if (undo_history.length == 1) {
406                   document.getElementById('undo').style.opacity = 0.4;
407                   document.getElementById('undo').style.filter = 'alpha(opacity=40)';
408               }
409           }
410       }
411
412       function initialize() {
413           map = new google.maps.Map(document.getElementById("mapbox"),
414                                     { mapTypeId: google.maps.MapTypeId.ROADMAP,
415                                       zoom: 2 });
416           var currently_dragging = false;
417           google.maps.event.addListener(map, "bounds_changed", function() {
418               if (!currently_dragging) {
419                   historyPush();
420               }
421           });
422           google.maps.event.addListener(map, "dragstart", function() {
423               currently_dragging = true;
424           });
425           google.maps.event.addListener(map, "dragend", function() {
426               historyPush();
427               currently_dragging = false;
428           });
429
430           mylat = 0;
431           mylng = 0;
432           var all_params = location.href.split("#")
433           if (all_params.length > 1 && all_params[1].length > 0) {
434               setFromState(all_params[1]);
435           } else {
436               <?php
437                   if ($latitude) {
438                ?>
439               mylat = <?php echo $latitude ?>;
440               mylng = <?php echo $longitude ?>;
441               var baseinfomessage = '<?php echo t('geolocated') ?>';
442               <?php
443                   } else {
444                ?>
445               var baseinfomessage = '<?php echo t('not.geolocated') ?>';
446               <?php
447                   }
448                ?>
449               map.setZoom(<?php echo $zoom ?>);
450               startUp(true);
451               info_window = new google.maps.InfoWindow({ content: baseinfomessage + '<?php echo t('home.icon.move.possible') ?>' });
452               info_window.open(map, me);
453               if (!document.cookie || document.cookie.indexOf("not-a-newbie") == -1) {
454                   document.getElementById("newbie").style.display = '';
455               }
456               var expires = new Date(new Date().getTime() + (10 * 86400000));  // 10 days
457               document.cookie = "not-a-newbie=true"
458                                 + "; expires=" + expires.toGMTString()
459                                 + "; path=/";
460           }
461       }
462
463     </script>
464 </head>
465
466 <body onload="initialize()">
467
468   <div id="mainbox" style="height: 100%">
469
470     <div id="debugbox"></div>
471
472     <div style="height: 2em">
473       <div style="float: right">
474         <?php echo t('travelmode') ?>
475         <select id="travelmode" onchange="travelmodeChanged()">
476           <option value="DRIVING"><?php echo t('travelmode.driving') ?></option>
477           <option value="WALKING"><?php echo t('travelmode.walking') ?></option>
478 <!-- doesn't work  <option value="google.maps.DirectionsTravelMode.BICYCLING">Bicycling</option> -->
479         </select>
480         - 
481         <input type="checkbox" id="fitmap" checked="true" onclick="fitmapChanged()"><?php echo t('fit.to.elements') ?>
482         - 
483         <a href="#" onclick="save()">
484           <?php echo t('save.this.page') ?>
485           <img src="images/save.png" class="iconwithtext"/>
486         </a>
487         -
488         <a href="#" onclick="historyPop()" id="undo" style="opacity:0.4;filter:alpha(opacity=40)">
489           <?php echo t('undo') ?>
490           <img src="images/undo.png" class="iconwithtext"/>
491         </a>
492         -
493         <?php if ($language != 'en') { ?>
494           <a href="?language=en"><img src="images/en.png" class="iconwithtext" style="opacity:0.4;filter:alpha(opacity=40)"/></a>
495         <?php } else { ?>
496           <a href="?language=en"><img src="images/en.png" class="iconwithtext"/></a>
497         <?php } ?>
498         <?php if ($language != 'fr') { ?>
499           <a href="?language=fr"><img src="images/fr.png" class="iconwithtext" style="opacity:0.4;filter:alpha(opacity=40)"/></a>
500         <?php } else { ?>
501           <a href="?language=fr"><img src="images/fr.png" class="iconwithtext"/></a>
502         <?php } ?>
503         <div id="savediv" style="display: none; position:absolute; right:1em; top:3em; background:#E0E0FF; border:solid 1px black; padding:0.5em; z-index:1">
504           <?php echo t('save.page.expl') ?>
505           <br/>
506           <input type="text" id="save" size="40"/>
507           <br/>
508           <div style="float:right"><a href="#" onclick="javascript:new Effect.Fade(document.getElementById('savediv'), {duration: 0.4})"><?php echo t('Okay') ?></a></div>
509         </div>
510       </div>
511
512       <form action="javascript:submit()">
513       <?php echo t('address') ?>
514       
515       <input type="text" size="32" id="address"/>
516       <script type="text/javascript">
517         document.getElementById('address').focus();
518       </script>
519       <input type="submit" value="<?php echo t('show.route') ?>"/>
520       <span id="submitshortresult" style="color: red">
521       </span>
522       </form>
523
524     </div>
525
526     <div id="usecases" style="display:none; background-color: #F77; border: solid 1px blue; position:absolute; top:10%; left:30%; right:30%; z-index:1">
527       <p style="margin:1em">
528         <?php echo t('use.cases.title') ?>
529       </p>
530       <p style="margin:1em">
531         <?php echo t('use.case.val') ?>
532       </p>
533       <p style="margin:1em">
534         <?php echo t('use.case.riviera') ?>
535       </p>
536       <div style="float:right; margin:1em"><a href="#" onclick="javascript:new Effect.Fade(document.getElementById('usecases'), {duration: 0.4})"><?php echo t('Okay') ?></a></div>
537     </div>
538
539     <div class="containermainbox">
540       <div style="float: right; width: 75%; height: 100%">
541          <div id="mapbox" style="width: 100%; height: 95%">
542          </div>
543          <div style="float:right; font-size:70%; margin-top:3px">
544            <em>
545              <?php echo t('bottomstuff') ?>
546            </em>
547          </div>
548       </div>
549       <div id="newbie" style="display: none; background-color: pink; border: solid 1px blue; position:absolute; width:20%; margin:1.5em">
550         <p align="right">
551           <img src="images/top.png"/>
552         </p>
553         <p align="right" style="margin-right:1em; margin-top:-1em">
554           <?php echo t('add.addess.top') ?>
555         </p>
556         <br/>
557         <p align="right">
558           <img src="images/right.png"/>
559         </p>
560         <p align="right" style="margin-right:1em; margin-top:-1em">
561           <?php echo t('routes.shown') ?>
562         </p>
563         <br/>
564         <br/>
565         <p align="right" style="margin-right:1em; margin-top:-1em">
566           <a href="#" onclick="javascript:new Effect.Fade(document.getElementById('newbie'), {duration: 0.4})"><?php echo t('Okay') ?></a>
567         </p>
568         <br/>
569         <p style="margin:1em">
570           <a href="#" onclick="javascript:new Effect.Appear(document.getElementById('usecases'), {duration: 0.4})"><?php echo t('show.use.cases') ?></a>
571         </p>
572       </div>
573       <ul id="resultslist">
574       </ul>
575     </div>
576
577   </div>
578
579 </body>
580 </html>