user2690888
user2690888

Reputation: 79

How to process text file into Google Maps waypoints + info text

I would like to use Google Maps to show a route using the location data and time stamp from a text file.
The text file (route.log) contains lines separated by unix line feeds and following this format: "Lat,Long;date, time".
Example:

52.1615470947258,20.805144309997561;14.03.2015, 17:33 52.15991486090931,20.804049968719483;14.03.2015, 17:37 52.15772967999426,20.805788040161133;14.03.2015, 17:47

The first line is the starting point, the last line is the end point, and each line inbetween a waypoint. And the time stamp (date, time) at the end of each line should be featured in the info text of each waypoint.

Now I've found a really great example of using Google Maps to show waypoints here.

My problem is:

how do I process the lines from the text file into a format (array) that is compatible with that Google Maps example? And how do I add the time stamp as to each marker info text?

And what if my text file has only 2 points (start + end) or maybe 1, will it throw an error or is there a 'failsafe'?

I'd be very grateful for help and hints. Thank you very much.



I've been trying to use AJAX/jQuery and here's the part of the code from the example where I'm stuck right now:

    <script>
  jQuery(function() {
    $.ajax({
url: "route.log",
dataType: 'text',
success: function (data) {
    var lines = data.match(/^.*((\r\n|\n|\r)|$)/gm);
    var stops = [];
    for (var i = 0; i < lines.length; i++) {
        var line = lines[i].replace("\n", "").split(";");
        stops.push(line);
    }
    //console.log(stops[0][1]);
}
});

var map = new window.google.maps.Map(document.getElementById("map"));
var directionsDisplay = new window.google.maps.DirectionsRenderer({suppressMarkers: true});
var directionsService = new window.google.maps.DirectionsService();

Tour_startUp(stops); // this here gets executed before the AJAX part has delivered the stops array > error

window.tour.loadMap(map, directionsDisplay);
window.tour.fitBounds(map);

if (stops.length > 1)
    window.tour.calcRoute(directionsService, directionsDisplay);       
});
function Tour_startUp(stops) {... // etc.

It seems that some code that needs the AJAX results (the stops array) is executed before AJAX has finished filling the stops array.

Also, in the referenced example code the stops arrays looks like this:

var stops = [
{"Geometry":{"Latitude":52.1615470947258,"Longitude":20.80514430999756}},
{"Geometry":{"Latitude":52.15991486090931,"Longitude":20.804049968719482}},
{"Geometry":{"Latitude":52.15772967999426,"Longitude":20.805788040161133}}]

If javascript could transform the text file lines into this format and add the timestamp as a third part, I could use the rest of the example code without the need of further modifying it a lot. Also I have an empty line at the end of the text file I need to get rid of (because it adds an empty array element).



EDIT 2:

This is the complete code incl. the latest changes:

<!doctype html>
    <?php
$fileContent = file('route.log', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
     foreach($fileContent as $line_num => $line) {
     { 
       $data = explode(";", rtrim($line));
       $geometry[] = explode(",", trim($data[0]));
       $timestamp[] = trim($data[1]);
     }
     }; 
    ?>

<html>
  <head>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
    <meta charset="utf-8">
    <title>Google Maps JavaScript API v3 Example: Directions Waypoints</title>
<style>
    #map{
    width: 100%;
    height: 450px;
}
</style>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
    <script src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
    <script>

     jQuery(function() {
        var stops = <?php
                echo '[ ';  
                foreach ($geometry as $row) {
                    echo '{"Geometry":{"Latitude":' . $row['0'] . ','. '"Longitude":' . $row['1'] . '}},' . "\n";
                }
                echo ' ]';              
        ?>;

    var map = new window.google.maps.Map(document.getElementById("map"));

    // new up complex objects before passing them around
    var directionsDisplay = new window.google.maps.DirectionsRenderer({suppressMarkers: true});
    var directionsService = new window.google.maps.DirectionsService();

    Tour_startUp(stops);

    window.tour.loadMap(map, directionsDisplay);
    window.tour.fitBounds(map);

    if (stops.length > 1)
        window.tour.calcRoute(directionsService, directionsDisplay);
});

function Tour_startUp(stops) {
    var stops_timestamp = JSON.parse( '<?php echo json_encode($timestamp) ?>' );

    if (!window.tour) window.tour = {
        updateStops: function (newStops) {
            stops = newStops;
        },
        // map: google map object
        // directionsDisplay: google directionsDisplay object (comes in empty)
        loadMap: function (map, directionsDisplay) {
            var myOptions = {
                zoom: 13,
                center: new window.google.maps.LatLng(51.507937, -0.076188), // default to London
                mapTypeId: window.google.maps.MapTypeId.ROADMAP
            };
            map.setOptions(myOptions);
            directionsDisplay.setMap(map);
        },
        fitBounds: function (map) {
            var bounds = new window.google.maps.LatLngBounds();

            // extend bounds for each record
            jQuery.each(stops, function (key, val) {
                var myLatlng = new window.google.maps.LatLng(val.Geometry.Latitude, val.Geometry.Longitude);
                bounds.extend(myLatlng);
            });
            map.fitBounds(bounds);
        },
        calcRoute: function (directionsService, directionsDisplay) {
            var batches = [];
            var itemsPerBatch = 10; // google API max = 10 - 1 start, 1 stop, and 8 waypoints
            var itemsCounter = 0;
            var wayptsExist = stops.length > 0;

            while (wayptsExist) {
                var subBatch = [];
                var subitemsCounter = 0;

                for (var j = itemsCounter; j < stops.length; j++) {
                    subitemsCounter++;
                    subBatch.push({
                        location: new window.google.maps.LatLng(stops[j].Geometry.Latitude, stops[j].Geometry.Longitude),
                        stopover: true
                    });
                    if (subitemsCounter == itemsPerBatch)
                        break;
                }

                itemsCounter += subitemsCounter;
                batches.push(subBatch);
                wayptsExist = itemsCounter < stops.length;
                // If it runs again there are still points. Minus 1 before continuing to
                // start up with end of previous tour leg
                itemsCounter--;
            }

            // now we should have a 2 dimensional array with a list of a list of waypoints
            var combinedResults;
            var unsortedResults = [{}]; // to hold the counter and the results themselves as they come back, to later sort
            var directionsResultsReturned = 0;

            for (var k = 0; k < batches.length; k++) {
                var lastIndex = batches[k].length - 1;
                var start = batches[k][0].location;
                var end = batches[k][lastIndex].location;

                // trim first and last entry from array
                var waypts = [];
                waypts = batches[k];
                waypts.splice(0, 1);
                waypts.splice(waypts.length - 1, 1);

                var request = {
                    origin: start,
                    destination: end,
                    waypoints: waypts,
                    travelMode: window.google.maps.TravelMode.WALKING
                };
                (function (kk) {
                    directionsService.route(request, function (result, status) {

                        if (status == window.google.maps.DirectionsStatus.OK) {

                            var unsortedResult = { order: kk, result: result };
                            unsortedResults.push(unsortedResult);

                            directionsResultsReturned++;

                            if (directionsResultsReturned == batches.length) // we've received all the results. put to map
                            {
                                // sort the returned values into their correct order
                                unsortedResults.sort(function (a, b) { return parseFloat(a.order) - parseFloat(b.order); });
                                var count = 0;
                                for (var key in unsortedResults) {
                                    if (unsortedResults[key].result != null) {
                                        if (unsortedResults.hasOwnProperty(key)) {
                                            if (count == 0) // first results. new up the combinedResults object
                                                combinedResults = unsortedResults[key].result;
                                            else {
                                                // only building up legs, overview_path, and bounds in my consolidated object. This is not a complete
                                                // directionResults object, but enough to draw a path on the map, which is all I need
                                                combinedResults.routes[0].legs = combinedResults.routes[0].legs.concat(unsortedResults[key].result.routes[0].legs);
                                                combinedResults.routes[0].overview_path = combinedResults.routes[0].overview_path.concat(unsortedResults[key].result.routes[0].overview_path);

                                                combinedResults.routes[0].bounds = combinedResults.routes[0].bounds.extend(unsortedResults[key].result.routes[0].bounds.getNorthEast());
                                                combinedResults.routes[0].bounds = combinedResults.routes[0].bounds.extend(unsortedResults[key].result.routes[0].bounds.getSouthWest());
                                            }
                                            count++;
                                        }
                                    }
                                }
                                directionsDisplay.setDirections(combinedResults);
                                var legs = combinedResults.routes[0].legs;
                                // alert(legs.length);
                                for (var i=0; i < legs.length;i++){
                  var markerletter = "A".charCodeAt(0);
                  markerletter += i;
                                  markerletter = String.fromCharCode(markerletter);
                                  createMarker(directionsDisplay.getMap(),legs[i].start_location,"Marker "+i,stops_timestamp[i]+"<br>"+legs[i].start_address,markerletter);
                                }
                                var i=legs.length;
                                var markerletter = "A".charCodeAt(0);
                    markerletter += i;
                                markerletter = String.fromCharCode(markerletter);
                                createMarker(directionsDisplay.getMap(),legs[legs.length-1].end_location,"Most Recent Position: Marker "+i,stops_timestamp[i]+"<br>"+legs[legs.length-1].end_address,markerletter);
                            }
                        }

                        //troubleshooting part follows
                        /*

                        else {
                                  // alert an error message when the route could nog be calculated.
                                  if (status == 'ZERO_RESULTS') {
                                    alert('No route could be found between the origin and destination.');
                                  } else if (status == 'UNKNOWN_ERROR') {
                                    alert('A directions request could not be processed due to a server error. The request may succeed if you try again.');
                                  } else if (status == 'REQUEST_DENIED') {
                                    alert('This webpage is not allowed to use the directions service.');
                                  } else if (status == 'OVER_QUERY_LIMIT') {
                                    alert('The webpage has gone over the requests limit in too short a period of time.');
                                  } else if (status == 'NOT_FOUND') {
                                    alert('At least one of the origin, destination, or waypoints could not be geocoded.');
                                  } else if (status == 'INVALID_REQUEST') {
                                    alert('The DirectionsRequest provided was invalid.');        
                                  } else {
                                    alert("There was an unknown error in your request. Requeststatus: nn"+status);
                                  }
                             }                 
                        //*/
                    });
                })(k);
            }
        }
    };
}
var infowindow = new google.maps.InfoWindow(
  { 
    size: new google.maps.Size(150,50)
  });

var icons = new Array();
icons["red"] = new google.maps.MarkerImage("mapIcons/marker_red.png",
      // This marker is 20 pixels wide by 34 pixels tall.
      new google.maps.Size(20, 34),
      // The origin for this image is 0,0.
      new google.maps.Point(0,0),
      // The anchor for this image is at 9,34.
      new google.maps.Point(9, 34));



function getMarkerImage(iconStr) {
   if ((typeof(iconStr)=="undefined") || (iconStr==null)) { 
      iconStr = "red"; 
   }
   if (!icons[iconStr]) {
      icons[iconStr] = new google.maps.MarkerImage("http://www.google.com/mapfiles/marker"+ iconStr +".png",
      // This marker is 20 pixels wide by 34 pixels tall.
      new google.maps.Size(20, 34),
      // The origin for this image is 0,0.
      new google.maps.Point(0,0),
      // The anchor for this image is at 6,20.
      new google.maps.Point(9, 34));
   } 
   return icons[iconStr];

}
  // Marker sizes are expressed as a Size of X,Y
  // where the origin of the image (0,0) is located
  // in the top left of the image.

  // Origins, anchor positions and coordinates of the marker
  // increase in the X direction to the right and in
  // the Y direction down.

  var iconImage = new google.maps.MarkerImage('mapIcons/marker_red.png',
      // This marker is 20 pixels wide by 34 pixels tall.
      new google.maps.Size(20, 34),
      // The origin for this image is 0,0.
      new google.maps.Point(0,0),
      // The anchor for this image is at 9,34.
      new google.maps.Point(9, 34));
  var iconShadow = new google.maps.MarkerImage('http://www.google.com/mapfiles/shadow50.png',
      // The shadow image is larger in the horizontal dimension
      // while the position and offset are the same as for the main image.
      new google.maps.Size(37, 34),
      new google.maps.Point(0,0),
      new google.maps.Point(9, 34));
      // Shapes define the clickable region of the icon.
      // The type defines an HTML &lt;area&gt; element 'poly' which
      // traces out a polygon as a series of X,Y points. The final
      // coordinate closes the poly by connecting to the first
      // coordinate.
  var iconShape = {
      coord: [9,0,6,1,4,2,2,4,0,8,0,12,1,14,2,16,5,19,7,23,8,26,9,30,9,34,11,34,11,30,12,26,13,24,14,21,16,18,18,16,20,12,20,8,18,4,16,2,15,1,13,0],
      type: 'poly'
  };


function createMarker(map, latlng, label, html, color) {
// alert("createMarker("+latlng+","+label+","+html+","+color+")");
    var contentString = '<b>'+label+'</b><br>'+html;
    var marker = new google.maps.Marker({
        position: latlng,
        map: map,
        shadow: iconShadow,
        icon: getMarkerImage(color),
        shape: iconShape,
        title: label,
        zIndex: Math.round(latlng.lat()*-100000)<<5
        });
        marker.myname = label;

    google.maps.event.addListener(marker, 'click', function() {
        infowindow.setContent(contentString); 
        infowindow.open(map,marker);
        });
    return marker;
}
    </script>
  </head>
  <body>
  <div id="map"></div>
</body>
</html>

It kind of works now. As you can see I used php to get the lines of the text into the correct format and into the stops Javascript array (+changing the file extension to index.php).

The only remaining problem is that sometimes the GoogleMaps JSON returns an Unknown Error and won't draw the route. It has something to do with certain geocoded locations. Some geocoordinates work, others don't. You can check for yourself, just add some coordinates into a text file name route.log and test it.

Upvotes: 0

Views: 2103

Answers (1)

MrUpsidown
MrUpsidown

Reputation: 22486

I will answer here the first part of your question which is how do I process the lines from the text file.

You will want to open your text file with an AJAX request. I strongly suggest that you use jQuery for that.

$.ajax({
    url: "route.log",
    dataType: 'text',
    success: function (data) {

        var lines = data.match(/^.*((\r\n|\n|\r)|$)/gm);

        for (var i = 0; i < lines.length; i++) {

            var line = lines[i].replace("\n", "").split(";");
            console.log(line);
        }
    }
});

This will output:

["52.1615470947258,20.805144309997561", "14.03.2015, 17:33"] ["52.15991486090931,20.804049968719483", "14.03.2015, 17:37"] ["52.15772967999426,20.805788040161133", "14.03.2015, 17:47"]

So basically, line[0] will be your lat/lng coordinates and line[1] your date/time information.

This way you can easily handle the different lines (start and end points, waypoints) and also check if you have enough lines to request Directions.

Hope this helps.

Edit:

Regarding the format of your data, you could do this way:

var stops = [];

$.ajax({
    url: "test.txt",
    dataType: 'text',
    success: function(data) {

        var lines = data.match(/^.*((\r\n|\n|\r)|$)/gm);

        for (var i = 0; i < lines.length; i++) {

            var line = lines[i].replace("\n", "").split(";");
            var geometry = line[0].split(",");

            stops.push({
                "Geometry": {
                    "Latitude": geometry[0],
                    "Longitude": geometry[1]
                },
                "Timestamp": line[1]
            });
        }

        console.log(stops);
    }
});

Upvotes: 1

Related Questions