3 // Copyright 2010 Guillaume Cottenceau.
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
9 // http://www.apache.org/licenses/LICENSE-2.0
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
17 ini_set("track_errors", 1);
18 ini_set('display_errors', 0);
20 // Helper function for fatal errors
21 function fatal($msg) {
22 echo "<h2>Error</h2>\n";
24 echo "</body></html>";
29 $host = 'api.hostip.info';
31 $path = '/get_html.php?position=true&ip=' . $_SERVER['REMOTE_ADDR'];
33 $fp = @fsockopen($host, $port, $errno, $errstr);
35 fatal("<p>Unable to open connection to <tt>$host</tt> ".
36 "(port <tt>$port</tt>):</p><pre>$errstr</pre>");
38 fwrite($fp, "GET $path HTTP/1.0\r\n");
39 fwrite($fp, "Host: $host:$port\r\n");
45 $buf .= fread($fp, 8192);
46 // Parse the HTTP answer and try to extract response code,
48 $answer = parse_answer($buf);
50 // We need to discover Content-Length, to know when server
51 // finishes sending data
53 if ($answer[0] == 200)
54 $length = $answer[1]['content-length'];
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.
61 if (strlen_b($buf) == strlen_b($answer[3]) + $length)
69 $latitude = $longitude = $zoom = 0;
70 if (preg_match('/Latitude: (\S+)/', $body, $matches)) {
71 $latitude = $matches[1];
74 if (preg_match('/Longitude: (\S+)/', $body, $matches)) {
75 $longitude = $matches[1];
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];
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
89 function strlen_b($str) {
90 if (function_exists('mb_orig_strlen')) {
91 return mb_orig_strlen($str);
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) {
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];
108 // Parse first line (server response status)
109 if (ereg("^HTTP/[0-9\.]+ ([0-9]+)", $response, $regs)) {
110 $response_code = $regs[1];
112 fatal("<p>Unable to parse response line <tt>$response</tt> ".
113 "from the server.</p>");
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];
120 fatal("<p>Unable to parse header line <tt>$line</tt> ".
122 "All headers:</p><pre>$headers_string</pre>");
126 // Return -1 as response code if parsing was not possible
129 return array($response_code, $headers, $body, $full_headers);
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">
145 // Copyright (c) Guillaume Cottenceau and licensed under the Apache 2.0 License
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;
154 var travelmode = google.maps.DirectionsTravelMode.DRIVING;
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);
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);
168 function travelmodeChanged() {
169 travelmode = eval(document.getElementById("travelmode").value);
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 )));
187 if (document.getElementById("fitmap").checked) {
188 map.fitBounds(bounds);
192 function showDirections() {
193 if (places.length > 0) {
194 bounds = new google.maps.LatLngBounds();
195 bounds.extend(new google.maps.LatLng(mylat, mylng));
197 for (var i = 0; i < places.length; i++) {
198 if (!places[i].directions_display.getMap()) {
199 showDirectionCapture(i);
201 bounds.extend(places[i].latlng);
206 function showDel(number) {
207 document.getElementById("delete" + number).style.visibility = 'visible';
210 function hideDel(number) {
211 document.getElementById("delete" + number).style.visibility = 'hidden';
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);
221 new_places.push(places[i]);
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,
232 icon:getIcon(iconname),
233 shadow:getShadow(iconname) });
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 ));
243 places.push({marker:marker, latlng:latlng, name:name, directions_display:directions_display});
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();
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);
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);
274 document.getElementById('savediv').style.display = '';
275 var save = document.getElementById('save');
276 save.value = 'http://routes-compare.zarb.org/' + link;
282 var me = new google.maps.Marker({position: new google.maps.LatLng(mylat, mylng),
285 icon: getIcon('icon31'),
286 shadow: getShadow('icon31')});
288 google.maps.event.addListener(me, "dragend", function(mouseEvent) {
289 mylat = mouseEvent.latLng.lat();
290 mylng = mouseEvent.latLng.lng();
293 map.setCenter(new google.maps.LatLng(mylat, mylng));
297 function initialize() {
298 map = new google.maps.Map(document.getElementById("mapbox"),
299 { mapTypeId: google.maps.MapTypeId.ROADMAP,
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') {
313 } else if (keyvalue[0] == 'p') {
314 createPlace(new google.maps.LatLng(values[0], values[1]), decodeURI(values[2]));
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/>';
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/>';
333 map.setZoom(<?php echo $zoom ?>);
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 = '';
342 var expires = new Date(new Date().getTime() + (10 * 86400000)); // 10 days
343 document.cookie = "not-a-newbie=true"
344 + "; expires=" + expires.toGMTString()
352 <body onload="initialize()">
354 <div id="mainbox" style="height: 100%">
356 <div id="debugbox"></div>
358 <div style="height: 2em">
359 <div style="float: right">
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> -->
367 <input type="checkbox" id="fitmap" checked="true" onchange="fitmap()">fit to elements
369 <a href="#" onclick="save()">
371 <img src="images/save.png" class="iconwithtext"/>
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:
376 <input type="text" id="save" size="40"/>
378 <div style="float:right"><a href="#" onclick="javascript:new Effect.Fade(document.getElementById('savediv'), {duration: 0.4})">Okay</a></div>
382 <form action="javascript:submit()">
385 <input type="text" size="32" id="address"/>
386 <script type="text/javascript">
387 document.getElementById('address').focus();
389 <input type="submit" value="Show route!"/>
390 <span id="submitshortresult" style="color: red">
397 <div class="containermainbox">
398 <div style="float: right; width: 75%; height: 100%">
399 <div id="mapbox" style="width: 100%; height: 95%">
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">
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>.
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>.
412 <div style="float:right; margin:1em"><a href="#" onclick="javascript:new Effect.Fade(document.getElementById('usecases'), {duration: 0.4})">Okay</a></div>
414 <div style="float:right; font-size:70%; margin-top:3px">
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>
423 <div id="newbie" style="display: none; background-color: pink; border: solid 1px blue; position:absolute; width:20%; margin:1.5em">
425 <img src="images/top.png"/>
427 <p align="right" style="margin-right:1em; margin-top:-1em">
428 1. add an address in the top area
432 <img src="images/right.png"/>
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
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>
444 <p style="margin:1em">
445 <a href="#" onclick="javascript:new Effect.Appear(document.getElementById('usecases'), {duration: 0.4})">Show me use cases!</a>
448 <ul id="resultslist">