2feae12f263a04eb2f3c39fdd53393e787532220
[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 // Helper function for fatal errors
21 function fatal($msg) {
22     echo "<h2>Error</h2>\n";
23     echo "$msg\n";
24     echo "</body></html>";
25     exit;
26
27 }
28
29 $host = 'api.hostip.info';
30 $port = 80;
31 $path = '/get_html.php?position=true&ip=' . $_SERVER['REMOTE_ADDR'];
32
33 $fp = @fsockopen($host, $port, $errno, $errstr);
34 if (!$fp)
35     fatal("<p>Unable to open connection to <tt>$host</tt> ".
36           "(port <tt>$port</tt>):</p><pre>$errstr</pre>");
37
38 fwrite($fp, "GET $path HTTP/1.0\r\n");
39 fwrite($fp, "Host: $host:$port\r\n");
40 fwrite($fp, "\r\n");
41
42 $buf = '';
43 $length = '';
44 while (!feof($fp)) {
45     $buf .= fread($fp, 8192);
46     // Parse the HTTP answer and try to extract response code,
47     // headers and body
48     $answer = parse_answer($buf);
49
50     // We need to discover Content-Length, to know when server
51     // finishes sending data
52     if (!$length) {
53         if ($answer[0] == 200)
54             $length = $answer[1]['content-length'];
55     }
56     
57     // When length is available, we can check if response has
58     // been fully transmitted; this is the case when all what
59     // we received equals the headers plus the full contents.
60     if ($length) {
61         if (strlen_b($buf) == strlen_b($answer[3]) + $length)
62             break;
63     }
64 }
65
66 fclose($fp);
67 $body = $answer[2];
68
69 $latitude = $longitude = $zoom = 0;
70 if (preg_match('/Latitude: (\S+)/', $body, $matches)) {
71     $latitude = $matches[1];
72     $zoom = 8;
73 }
74 if (preg_match('/Longitude: (\S+)/', $body, $matches)) {
75     $longitude = $matches[1];
76 }
77 if (($latitude == 0 || $longitude == 0) && preg_match('/Country: .*?\((\S{2})\)/', $body, $matches)) {
78     $country = $matches[1];
79     include('countries.phpi');
80     $latitude = $countries[$country][0];
81     $longitude = $countries[$country][1];
82     $zoom = 5;
83 }
84
85 // PHP's strlen should return the length of a string in bytes,
86 // except when using multibyte strings, in which case
87 // mb_orig_strlen must be used (in case strlen was overloaded
88 // with mb_strlen).
89 function strlen_b($str) {
90     if (function_exists('mb_orig_strlen')) {
91         return mb_orig_strlen($str);
92     } else {
93         return strlen($str);
94     }
95 }
96
97 // Parse an HTTP answer to extract response code, headers and body
98 // Returns an array containing: response code, headers, body, raw headers
99 function parse_answer($answer) {
100
101     // This first pattern matching allows to separate first line
102     // (server response status), headers, and data contents 
103     if (ereg("^(([^\n]+)\r\n(.*)\r\n\r\n)(.*)", $answer, $regs)) {
104         $full_headers   = $regs[1];
105         $response       = $regs[2];
106         $headers_string = $regs[3];
107         $body           = $regs[4];
108         // Parse first line (server response status)
109         if (ereg("^HTTP/[0-9\.]+ ([0-9]+)", $response, $regs)) {
110             $response_code = $regs[1];
111         } else {
112             fatal("<p>Unable to parse response line <tt>$response</tt> ".
113                   "from the server.</p>");
114         }
115         // Parse headers and build a hash with them
116         foreach (split("\r\n", $headers_string) as $line) {
117             if (ereg("^([^:]+): (.*)", $line, $regs)) {
118                 $headers[strtolower($regs[1])] = $regs[2];
119             } else {
120                 fatal("<p>Unable to parse header line <tt>$line</tt> ".
121                       "from the server. ".
122                       "All headers:</p><pre>$headers_string</pre>");
123             }
124         }
125     } else {
126         // Return -1 as response code if parsing was not possible
127         return array(-1);
128     }
129     return array($response_code, $headers, $body, $full_headers);
130 }
131
132 ?>
133
134 <html>
135 <head>
136     <title>Routes Compare</title>
137     <link rel="stylesheet" type="text/css" media="screen" href="style.css"/>
138     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
139     <script type="text/javascript" src="prototype.js"></script>
140     <script type="text/javascript" src="effects.js"></script>
141     <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
142     <script type="text/javascript" src="googlemaps.js"></script>
143     <script type="text/javascript">
144
145       // Copyright (c) Guillaume Cottenceau and licensed under the Apache 2.0 License
146
147       var mylat, mylng;
148       var map;
149       var places = new Array();
150       var geocoder = new google.maps.Geocoder();
151       var directions_service = new google.maps.DirectionsService();
152       var info_window = null;
153       var bounds;
154       var travelmode = google.maps.DirectionsTravelMode.DRIVING;
155
156       function reload(new_places) {
157           for (var i = 0; i < places.length; i++) {
158               places[i].marker.setMap(null);
159               places[i].directions_display.setMap(null);
160           }
161           places = new Array();
162           document.getElementById("resultslist").innerHTML = '';
163           for (var i = 0; i < new_places.length; i++) {
164               createPlace(new_places[i].latlng, new_places[i].name);
165           }
166       }
167
168       function travelmodeChanged() {
169           travelmode = eval(document.getElementById("travelmode").value);
170           reload(places);
171       }
172
173       function showDirectionCapture(i) {
174           var request = { origin: new google.maps.LatLng(mylat, mylng),
175                           destination: places[i].latlng,
176                           travelMode: travelmode };
177           directions_service.route(request, function(result, status) {
178               if (status == google.maps.DirectionsStatus.OK) {
179                   places[i].directions_display.setMap(map);
180                   places[i].directions_display.setDirections(result);
181                   displayDirectionsMoreinfo(result.routes[0], document.getElementById("directions" + ( 1 + i )));
182               }
183           });
184       }
185       
186       function fitmap() {
187           if (document.getElementById("fitmap").checked) {
188               map.fitBounds(bounds);
189           }
190       }
191
192       function showDirections() {
193           if (places.length > 0) {
194               bounds = new google.maps.LatLngBounds();
195               bounds.extend(new google.maps.LatLng(mylat, mylng));
196           }
197           for (var i = 0; i < places.length; i++) {
198               if (!places[i].directions_display.getMap()) {
199                   showDirectionCapture(i);
200               }
201               bounds.extend(places[i].latlng);
202           }
203           fitmap();
204       }
205
206       function showDel(number) {
207           document.getElementById("delete" + number).style.visibility = 'visible';
208       }
209
210       function hideDel(number) {
211           document.getElementById("delete" + number).style.visibility = 'hidden';
212       }
213
214       function del(number) {
215           var new_places = new Array();
216           for (var i = 0; i < places.length; i++) {
217               if (i == number - 1) {
218                   places[i].marker.setMap(null);
219                   places[i].directions_display.setMap(null);
220               } else {
221                   new_places.push(places[i]);
222               }
223           }
224           reload(new_places);
225       }
226
227       function createPlace(latlng, name) {
228           var number = places.length + 1;
229           var iconname = 'icon' + ( 7 + number );
230           var marker = new google.maps.Marker({ position: latlng,
231                                                 title:name,
232                                                 icon:getIcon(iconname),
233                                                 shadow:getShadow(iconname) });
234           marker.setMap(map);
235           document.getElementById("resultslist").innerHTML += "<li onmouseover='showDel(" + number + ")' onmouseout='hideDel(" + number + ")'><img class='iconwithtext' src='images/" + iconname + ".png'/> " + name
236                                                               + "<div style='float:right; padding:3; visibility:hidden' id='delete" + number
237                                                               + "'><a href='#' onclick='del(" + number + ")'><img src='images/bin.png'/></a></div>"
238                                                               + "<br/><span id='directions" + number + "'></span></li>";
239           var directions_display = new google.maps.DirectionsRenderer({ preserveViewport: true, suppressMarkers: true, draggable: true });
240           google.maps.event.addListener(directions_display, "directions_changed", function() {
241               displayDirectionsMoreinfo(directions_display.getDirections().routes[0], document.getElementById("directions" + number ));
242           });
243           places.push({marker:marker, latlng:latlng, name:name, directions_display:directions_display});
244           showDirections();
245       }
246
247       function submit() {
248           if (info_window) {
249               info_window.close();
250               info_window = null;
251           }
252           new Effect.Fade(document.getElementById('newbie'), {duration: 0.4});
253           new Effect.Fade(document.getElementById('usecases'), {duration: 0.4});
254           var address = document.getElementById('address').value;
255           geocoder.geocode({ 'address': address },
256                            function(results, status) {
257                                if (status == google.maps.GeocoderStatus.OK) {
258                                    createPlace(results[0].geometry.location, address);
259                                    document.getElementById('address').value = '';
260                                    document.getElementById('address').focus();
261                                } else {
262                                    document.getElementById('submitshortresult').innerHTML = "Address not found...";
263                                    document.getElementById('submitshortresult').style.display = '';
264                                    setTimeout('new Effect.Fade(document.getElementById("submitshortresult"), {duration: 0.4})', 1000);
265                                }
266                            });
267       }
268
269       function save() {
270           var link = "#me=" + mylat + ";" + mylng;
271           for (var i = 0; i < places.length; i++) {
272               link += "&p=" + places[i].latlng.lat() + ";" + places[i].latlng.lng() + ";" + encodeURI(places[i].name);
273           }
274           document.getElementById('savediv').style.display = '';
275           var save = document.getElementById('save');
276           save.value = 'http://routes-compare.zarb.org/' + link;
277           save.focus();
278           save.select();
279       }
280
281       function startUp() {
282           var me = new google.maps.Marker({position: new google.maps.LatLng(mylat, mylng),
283                                            title: 'Me',
284                                            draggable: true,
285                                            icon: getIcon('icon31'),
286                                            shadow: getShadow('icon31')});
287           me.setMap(map);
288           google.maps.event.addListener(me, "dragend", function(mouseEvent) {
289                   mylat = mouseEvent.latLng.lat();
290                   mylng = mouseEvent.latLng.lng();
291                   reload(places);
292               });
293           map.setCenter(new google.maps.LatLng(mylat, mylng));
294           return me;
295       }
296                          
297       function initialize() {
298           map = new google.maps.Map(document.getElementById("mapbox"),
299                                     { mapTypeId: google.maps.MapTypeId.ROADMAP,
300                                       zoom: 2 });
301
302           mylat = 0;
303           mylng = 0;
304           var all_params = location.href.split("#")
305           if (all_params.length > 1 && all_params[1].length > 0) {
306               var params = all_params[1].split("&");
307               for (i = 0; i < params.length; i++) {
308                   var keyvalue = params[i].split("=");
309                   var values = keyvalue[1].split(";");
310                   if (keyvalue[0] == 'me') {
311                       mylat = values[0];
312                       mylng = values[1];
313                   } else if (keyvalue[0] == 'p') {
314                       createPlace(new google.maps.LatLng(values[0], values[1]), decodeURI(values[2]));
315                   }
316               }
317               startUp();
318           } else {
319               <?php
320                   if ($latitude) {
321                ?>
322               mylat = <?php echo $latitude ?>;
323               mylng = <?php echo $longitude ?>;
324               var baseinfomessage = 'This is where I located you,<br/>based on your IP address.<br/>';
325               <?php
326                   } else {
327                ?>
328               var baseinfomessage = 'It was not possible to locate<br/>you, based on your IP address<br/>'
329                                   + '<a href="http://www.hostip.info/" target="_blank">(submit your location!)</a>.<br/>';
330               <?php
331                   }
332                ?>
333               map.setZoom(<?php echo $zoom ?>);
334               me = startUp();
335               info_window = new google.maps.InfoWindow({ content: baseinfomessage
336                                                                   + 'You can drag this icon<br/>to move your position.<br/>'
337                                                                   + '<div class="fakelink" style="float: right" onclick="javascript:info_window.close()">Okay</div>' });
338               info_window.open(map, me);
339               if (!document.cookie || document.cookie.indexOf("not-a-newbie") == -1) {
340                   document.getElementById("newbie").style.display = '';
341               }
342               var expires = new Date(new Date().getTime() + (10 * 86400000));  // 10 days
343               document.cookie = "not-a-newbie=true"
344                                 + "; expires=" + expires.toGMTString()
345                                 + "; path=/";
346           }
347       }
348
349     </script>
350 </head>
351
352 <body onload="initialize()">
353
354   <div id="mainbox" style="height: 100%">
355
356     <div id="debugbox"></div>
357
358     <div style="height: 2em">
359       <div style="float: right">
360         travel mode:
361         <select id="travelmode" onchange="travelmodeChanged()">
362           <option value="google.maps.DirectionsTravelMode.DRIVING">driving</option>
363           <option value="google.maps.DirectionsTravelMode.WALKING">walking</option>
364 <!-- doesn't work  <option value="google.maps.DirectionsTravelMode.BICYCLING">Bicycling</option> -->
365         </select>
366         - 
367         <input type="checkbox" id="fitmap" checked="true" onchange="fitmap()">fit to elements
368         - 
369         <a href="#" onclick="save()">
370           save this page
371           <img src="images/save.png" class="iconwithtext"/>
372         </a>
373         <div id="savediv" style="display: none; position:absolute; right:1em; top:3em; background:#E0E0FF; border:solid 1px black; padding:0.5em; z-index:1">
374           The following link will directly present the current data:
375           <br/>
376           <input type="text" id="save" size="40"/>
377           <br/>
378           <div style="float:right"><a href="#" onclick="javascript:new Effect.Fade(document.getElementById('savediv'), {duration: 0.4})">Okay</a></div>
379         </div>
380       </div>
381
382       <form action="javascript:submit()">
383       Address:
384       
385       <input type="text" size="32" id="address"/>
386       <script type="text/javascript">
387         document.getElementById('address').focus();
388       </script>
389       <input type="submit" value="Show route!"/>
390       <span id="submitshortresult" style="color: red">
391       </span>
392       </form>
393
394     </div>
395
396
397     <div class="containermainbox">
398       <div style="float: right; width: 75%; height: 100%">
399          <div id="mapbox" style="width: 100%; height: 95%">
400          </div>
401          <div id="usecases" style="display:none; background-color: #F77; border: solid 1px blue; position:fixed; top:10%; left:30%; right:30%; z-index:1">
402            <p style="margin:1em">
403              Use cases:
404            </p>
405            <p style="margin:1em">
406              You live near Gloucester Road in London; you know that Val d'Isère in France and Verbier in Switzerland are two great ski destinations and you would like to <a href="http://routes-compare.zarb.org/#me=51.492758928160555;-0.181784183374063&p=45.4487241;6.9803461;Val%20d'Is%C3%A8re&p=46.09614;7.21909;Verbier" target="_blank">compare routes</a>.
407            </p>
408            <p style="margin:1em">
409            You know you want to visit three locations in France: Nice, Monaco and Menton, and you're looking
410         for a small village where to spend a few nights - move the little home icon and see that <a href="http://routes-compare.zarb.org/#me=43.753611471382484;7.382017588330072&p=43.7034273;7.2662656;nice&p=43.7384176;7.4246158;monaco&p=43.7788464;7.505666;menton" target="_blank">Laghet seems nicely located not far from all destinations</a>.
411            </p>
412            <div style="float:right; margin:1em"><a href="#" onclick="javascript:new Effect.Fade(document.getElementById('usecases'), {duration: 0.4})">Okay</a></div>
413          </div>
414          <div style="float:right; font-size:70%; margin-top:3px">
415            <em>
416              uses goodies from <a href="http://maps.google.com/" target="_blank">Google Maps</a>, <a href="http://script.aculo.us/" target="_blank">Scriptaculous</a> and <a href="http://hostip.info/" target="_blank">hostip.info</a>, and is glued together by
417              <a href="http://zarb.org/~gc/" target="_blank">Guillaume Cottenceau</a>
418              - <a href="#" onclick="javascript:new Effect.Appear(document.getElementById('usecases'), {duration: 0.4})">use cases</a>
419              - <a href="http://git.zarb.org/cgi-bin/gitweb.cgi?p=routes-compare;a=summary" target="_blank">source</a>
420            </em>
421          </div>
422       </div>
423       <div id="newbie" style="display: none; background-color: pink; border: solid 1px blue; position:absolute; width:20%; margin:1.5em">
424         <p align="right">
425           <img src="images/top.png"/>
426         </p>
427         <p align="right" style="margin-right:1em; margin-top:-1em">
428           1. add an address in the top area
429         </p>
430         <br/>
431         <p align="right">
432           <img src="images/right.png"/>
433         </p>
434         <p align="right" style="margin-right:1em; margin-top:-1em">
435           2. the routes from all added addresses to the "home"
436           icon will be shown on the map
437         </p>
438         <br/>
439         <br/>
440         <p align="right" style="margin-right:1em; margin-top:-1em">
441           <a href="#" onclick="javascript:new Effect.Fade(document.getElementById('newbie'), {duration: 0.4})">Okay</a>
442         </p>
443         <br/>
444         <p style="margin:1em">
445           <a href="#" onclick="javascript:new Effect.Appear(document.getElementById('usecases'), {duration: 0.4})">Show me use cases!</a>
446         </p>
447       </div>
448       <ul id="resultslist">
449       </ul>
450     </div>
451
452   </div>
453
454 </body>
455 </html>