Unfolding Maps: How to map separate lat/long coordinates in an array to screen.

edited April 2018 in Library Questions

I've constructed this code based on the principles of Daniel Schiffmans 'exercise_18_15_new_bubbles_xml.pde' on Github.

All is going well, except that I am having difficulty applying it using Unfolding Maps to display train locations from an XML file.

I wish to display either a 'SimplePointMarker' (from the Unfolding Maps library) or a simple ellipse for each train location. My difficulty is with the mapping. I can map one train with no issues but unsure how to map all within the array.

I have tried a few things like making an array of train locations separately and making another object to handle mapping but both have failed.

The code is posted below. The core issue lies on line 80.

The XML data is contained in this link. I copied it to a static XML file: https://data.smartdublin.ie/cgi-bin/rtpi/realtimebusinformation?stopid=7602&format=xml

Any help would be greatly appreciated. Thank you :)

*I edited the code to be more simple in my response below

Answers

  • Here is a simplified version using a static XML file rather than a stream. The text from the xml file is posted directly below the code.

    As a stop gap, have mapped the coordinates to roughly the correct locations. But once you zoom in/out, this mapping does not correspond.

    Anyone with any tips on how to get this to work with 'Simplepointmarkers' or other wise where the coordinates are preserved regardless of panning/zooming would be much appreciated. Thank you :)

    import de.fhpotsdam.unfolding.*;
    import de.fhpotsdam.unfolding.geo.*;
    import de.fhpotsdam.unfolding.utils.*;  
    import de.fhpotsdam.unfolding.marker.*;
    
    UnfoldingMap map;
    DebugDisplay debugDisplay;
    
    Train_Class[] trains = new Train_Class[10];
    
    XML xml;   // A Table object
    
    
    void setup() {
      size(1024, 768, P2D);
    
      map = new UnfoldingMap(this, "myMap");
      map.zoomAndPanTo(new Location(53.0980f, -7.9097f), 7);
      MapUtils.createDefaultEventDispatcher(this, map);
      debugDisplay = new DebugDisplay(this, map);
    
      for (int i = 0; i < 10; i++) {
        trains[i] = new Train_Class(width/2, height/2, random(100));
      }
    }
    
    
    
    void draw() {
      map.draw();
      debugDisplay.draw();
    
      xml = loadXML("xml.xml");   // Load XML file
    
      XML[] children = xml.getChildren("objTrainPositions");    // Get all the child nodes named "objTrainPositions"
    
      for (int i = 0; i < 10; i++) {  
    
        XML trainCodeElement = children[i].getChild("TrainCode");
        String trainCode = trainCodeElement.getContent();  
    
        XML latitudeElement = children[i].getChild("TrainLatitude");
        float latitude = latitudeElement.getFloatContent();   
    
        XML longitudeElement = children[i].getChild("TrainLongitude");
        float longitude = longitudeElement.getFloatContent();  
    
        println("Train Code: " + trainCode + ", Latitude: " + latitude + ", Longitude: " + longitude);
    
        float mappedLatitude = map(latitude, 55.557, 50.497, 0, height);
        float mappedLongitude = map(longitude, -13.535, -2.296, 0, width);
        trains[i].display(mappedLongitude, mappedLatitude);
      }
    }
    
    
    class Train_Class {
      float x, y;
      float colour;
    
      // Create  the Train
      Train_Class (float x_, float y_, float colour_) {
        x = x_;
        x = y_;
        colour = colour_;
      }
    
      void display(float posX, float posY) {    // Display the Train
        x = posX;
        y = posY;
        fill(colour);
        stroke(255,0,0);
        ellipse(x, y, 4, 4);
      }
    }
    
    
    <ArrayOfObjTrainPositions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://api.irishrail.ie/realtime/">
        <objTrainPositions>
            <TrainStatus>N</TrainStatus>
            <TrainLatitude>53.1442</TrainLatitude>
            <TrainLongitude>-6.06085</TrainLongitude>
            <TrainCode>E917</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>...</PublicMessage>
            <Direction>Northbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.2557</TrainLatitude>
            <TrainLongitude>-6.11317</TrainLongitude>
            <TrainCode>E214</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>...</PublicMessage>
            <Direction>Southbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.2756</TrainLatitude>
            <TrainLongitude>-6.10333</TrainLongitude>
            <TrainCode>E916</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>...</PublicMessage>
            <Direction>Northbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.2991</TrainLatitude>
            <TrainLongitude>-6.16512</TrainLongitude>
            <TrainCode>E108</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>...</PublicMessage>
            <Direction>Southbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.3206</TrainLatitude>
            <TrainLongitude>-6.21112</TrainLongitude>
            <TrainCode>E808</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>
                E808\n17:10 - Greystones to Malahide (1 mins late)\nArrived Sydney Parade next stop Sandymount
            </PublicMessage>
            <Direction>Northbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.3531</TrainLatitude>
            <TrainLongitude>-6.24591</TrainLongitude>
            <TrainCode>E215</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>
                E215\n17:30 - Howth to Bray (-3 mins late)\nArrived Dublin Connolly next stop Tara Street
            </PublicMessage>
            <Direction>Southbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.3629</TrainLatitude>
            <TrainLongitude>-6.22753</TrainLongitude>
            <TrainCode>E915</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>
                E915\n17:00 - Bray to Howth (3 mins late)\nDeparted Clontarf Road next stop Killester
            </PublicMessage>
            <Direction>Northbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>R</TrainStatus>
            <TrainLatitude>53.392</TrainLatitude>
            <TrainLongitude>-6.11448</TrainLongitude>
            <TrainCode>E216</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>
                E216\n17:45 - Howth to Bray (1 mins late)\nDeparted Sutton next stop Bayside
            </PublicMessage>
            <Direction>Southbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>T</TrainStatus>
            <TrainLatitude>53.1442</TrainLatitude>
            <TrainLongitude>-6.06085</TrainLongitude>
            <TrainCode>E107</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>
                E107\n16:27 - Howth to Greystones(2 mins late)\nTERMINATED Greystones at 17:47
            </PublicMessage>
            <Direction>Southbound</Direction>
        </objTrainPositions>
        <objTrainPositions>
            <TrainStatus>T</TrainStatus>
            <TrainLatitude>53.4509</TrainLatitude>
            <TrainLongitude>-6.15649</TrainLongitude>
            <TrainCode>E807</TrainCode>
            <TrainDate>02 Apr 2018</TrainDate>
            <PublicMessage>
                E807\n16:43 - Bray to Malahide(0 mins late)\nTERMINATED Malahide at 17:50
            </PublicMessage>
            <Direction>Northbound</Direction>
        </objTrainPositions>
    </ArrayOfObjTrainPositions>
    
  • I did not try out your code, but a couple of remarks on first sight:

    • It is not good practice to load or parse data in the draw-method, as this results in doing that each frame, which is very slow and unnecessary.
    • If you are using Unfolding there is no need to map geo-locations to the canvas yourself. That's one of the core features of the library.
    • I would suggest to load all trains into an array (as you do), and then draw each one in draw() by iterating over them and converting from their location to the screen position manually. Try ScreenPosition pos = map.getScreenPosition(trains[i].location) and draw something at that screen position.

    Hope that helps.

  • Thanks tnagel. I know that is a core built in feature. Maybe I didn't phrase the question well. Basically I am having trouble putting it into an array and making it work like you describe.

    The Unfolding maps library example files seem to deal with various methods of placing arrays of markers based on CSV, Json and RSS. However, my example is using an XML feed. Anything I try based on those examples seems to fail. That's why I haphazardly arranged the previous code as a workaround while I try to get it to work.

    Sorry if I was unclear. Thanks :)

  • edited April 2018

    I tried another technique. No joy. I tried to follow along with this tutorial, and did my best to substitute the CSV techniques he uses for XML. https://vimeo.com/90091197

    I deleted the trains object to simplify. It should simply print out the static XML coordinates to the map.

    I can get a print out of the readings (line 62) no problem, but the markers do not print to screen. println("Train Code: " + trainCode + ", Latitude: " + latitude + ", Longitude: " + longitude);

    One thing to note is that when the "StamenMapProvider.Toner" (line 39) was included it threw up an error saying the it did not exist. Also the 'OPENGL' in the size function threw an error so I changed it to 'P2D', as that seems to work in all other unfolding maps.

    I'm not sure where I am going wrong. My code is as follows:

    import de.fhpotsdam.unfolding.mapdisplay.*;
    import de.fhpotsdam.unfolding.utils.*;  
    import de.fhpotsdam.unfolding.marker.*;
    import de.fhpotsdam.unfolding.tiles.*;
    import de.fhpotsdam.unfolding.interactions.*; 
    import de.fhpotsdam.unfolding.ui.*;
    import de.fhpotsdam.unfolding.*;
    import de.fhpotsdam.unfolding.core.*;  
    import de.fhpotsdam.unfolding.mapdisplay.shaders.*;
    import de.fhpotsdam.unfolding.data.*;
    import de.fhpotsdam.unfolding.geo.*; 
    import de.fhpotsdam.unfolding.texture.*; 
    import de.fhpotsdam.unfolding.events.*; 
    import de.fhpotsdam.utils.*; 
    import de.fhpotsdam.unfolding.providers.*;
    
    //Train_Class[] trains = new Train_Class[10];
    
    UnfoldingMap map;
    SimplePointMarker berlinMarker;
    String[] paths;
    XML xml;   
    
    Location nycLoc;
    
    
    void setup() {
      size(800, 600, P2D);
      processData();
    }
    
    
    void draw() {
      map.draw();
    }
    
    
    void processData(){
      map = new UnfoldingMap(this); //, new StamenMapProvider.Toner());
      nycLoc = new Location(53.098, -7.910);   
      map.zoomAndPanTo(nycLoc, 7);
    
      xml = loadXML("xml.xml");   // Load XML file
      XML[] children = xml.getChildren("objTrainPositions");    // Get all the child nodes named "objTrainPositions"
    
      for (int i = 0; i < children.length; i++) {     //for (int i = 0; i < trains.length; i++) {
        XML trainCodeElement = children[i].getChild("TrainCode");
        String trainCode = trainCodeElement.getContent();  
    
        XML latitudeElement = children[i].getChild("TrainLatitude");
        float latitude = latitudeElement.getFloatContent();   
    
        XML longitudeElement = children[i].getChild("TrainLongitude");
        float longitude = longitudeElement.getFloatContent();  
    
        Location thisLocation = new Location(longitude, latitude);
        SimplePointMarker here = new SimplePointMarker(thisLocation);
        here.setColor( color(255,0,0,100) );
        here.setStrokeWeight(0); 
        map.addMarker(here);
    
        println("Train Code: " + trainCode + ", Latitude: " + latitude + ", Longitude: " + longitude);     
      }
      MapUtils.createDefaultEventDispatcher(this,map);
    }
    
  • edited April 2018

    I tried a different techique based on your reply to a query from a user about appling a SQL database to the application: https://forum.processing.org/two/discussion/9300/mapping-coordinates-from-database-using-unfolding

    This seems to work but I'll now try and get timer running to update it. Here the code, in case it's of use to anyone in future:

    // Attribution:  
    // https://forum.processing.org/two/discussion/9300/mapping-coordinates-from-database-using-unfolding
    
    import de.fhpotsdam.unfolding.*;
    import de.fhpotsdam.unfolding.geo.*;
    import de.fhpotsdam.unfolding.utils.*;
    import de.fhpotsdam.unfolding.marker.*;
    
    UnfoldingMap map;
    XML xml;   
    Location irelandLoc;
    
    
    void setup() {
      size(800, 600, P2D);
      map = new UnfoldingMap(this);
      MapUtils.createDefaultEventDispatcher(this, map);
      irelandLoc = new Location(53.098, -7.910);   
      map.zoomAndPanTo(irelandLoc, 7);
    
      // Load the XML feed from URL
      xml = loadXML("http://api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML");  
    
      XML[] children = xml.getChildren("objTrainPositions");    // Get all the child nodes named "objTrainPositions"
    
    
      for (int i = 0; i < children.length; i++) {     
        XML trainCodeElement = children[i].getChild("TrainCode");
        String trainCode = trainCodeElement.getContent();  
    
        XML latitudeElement = children[i].getChild("TrainLatitude");
        float latitude = latitudeElement.getFloatContent();   
    
        XML longitudeElement = children[i].getChild("TrainLongitude");
        float longitude = longitudeElement.getFloatContent();  
    
        Location location = new Location(latitude, longitude);
        println(trainCode + ": " + location);
    
        map.addMarker(new SimplePointMarker(location));  // Create Unfolding Marker here
      }
    } 
    
    
    void draw() {
      map.draw();
    }
    
  • edited April 2018

    And now all seems to update and run okay based on a timer object:

    import de.fhpotsdam.unfolding.*;
    import de.fhpotsdam.unfolding.geo.*;
    import de.fhpotsdam.unfolding.utils.*;
    import de.fhpotsdam.unfolding.marker.*;
    
    UnfoldingMap map;
    XML xml;   
    XML[] children;
    Location irelandLoc;
    
    Timer timer = new Timer(1000);
    
    
    void setup() {
      size(800, 600, P2D);
      map = new UnfoldingMap(this);
      MapUtils.createDefaultEventDispatcher(this, map);
      irelandLoc = new Location(53.098, -7.910);   
      map.zoomAndPanTo(irelandLoc, 7);
    
      xml = loadXML("xml.xml");   // Load XML file
      children = xml.getChildren("objTrainPositions");    // Get all the child nodes named "objTrainPositions"  
    } 
    
    
    void requestData() {   
      for (int i = 0; i < children.length; i++) {     
        XML trainCodeElement = children[i].getChild("TrainCode");
        String trainCode = trainCodeElement.getContent();  
    
        XML latitudeElement = children[i].getChild("TrainLatitude");
        float latitude = latitudeElement.getFloatContent();   
    
        XML longitudeElement = children[i].getChild("TrainLongitude");
        float longitude = longitudeElement.getFloatContent();  
    
        Location location = new Location(latitude, longitude);
        println(trainCode + ": " + location);
    
        map.addMarker(new SimplePointMarker(location));  // Create Unfolding Marker here
      }
    } 
    
    
    
    void draw() {
      map.draw();
    
      //xml = loadXML("xml.xml");    // Load the XML feed from URL  
      xml = loadXML("http://" + "api.irishrail.ie/realtime/realtime.asmx/getCurrentTrainsXML");    // Load the XML feed from URL
    
      children = xml.getChildren("objTrainPositions");    // Get all the child nodes named "objTrainPositions"  
    
      if (timer.isFinished()) {   // Every one second, make a new request.
        requestData();
        timer.start();  // And restart the timer.
      }
      thread("requestData");  
    }
    
    
    
    class Timer {
      int savedTime;
      boolean running = false;
      int totalTime;
    
      Timer(int tempTotalTime) {
        totalTime = tempTotalTime;
      }
    
      void start() {
        running = true;
        savedTime = millis();
      }
    
      boolean isFinished() {
        int passedTime = millis() - savedTime;
        if (running && passedTime > totalTime) {
          running = false;
          return true;
        } else {
          return false;
        }
      }
    }
    
Sign In or Register to comment.