I can read Serial but How do I use offline maps??

edited June 2014 in Library Questions

I'm new to programming and most of what I do is copied and not from any thorough knowledge of programming. I know a little when I need to know a lot more to do what I want.

I'm trying to use a serial connection to move a map. To do this I found the modestmaps library and the example translates the mouse position to move a map. That's neat, but I want to be able to move the map with GPS over serial. I have an arduino program that can read a GPS rx and output the lat and lon in a buffer. I broke the connector though and don't have GPS at the moment so I just tried to test the use of serial with the modestmaps.

Here's what happened, I decided to send attitude X,Y,Z and get it to act like a mouse by translating X and Y by converting using the map(); That seems to work to get a mouselike coordinate, but I can't do much with it. I can get modestmaps to display the arduino XYZ and I can get it to keep the map grabbed.

What I couldn't figure out at first is how to get the modestmaps to see that a change in the arduino XYZ is the same as a mouse movement. I got over that hurdle and can now read GPS. The parsing is done on the Arduino and lat and lon is sent as an array.

Here's all the borrowed code that's currently working to read serial:

 import processing.opengl.*;
import com.modestmaps.*;
import com.modestmaps.core.*;
import com.modestmaps.geo.*;
import com.modestmaps.providers.*;
import processing.serial.*;

Serial myPort;  // Create object from Serial class

final String serialPort = "COM4"; // replace this with your serial port. On windows you will need something like "COM1".
//final String serialPort = "COM4"; // replace this with your serial port. On windows you will need something like "COM1".

float [] q = new float [4];
float [] hq = null;
float [] Euler = new float [3]; // psi, theta, phi

float newX = q[0] / 10000000;
float newY = q[1] / 10000000;  


int lf = 10; // 10 is '\n' in ASCII
byte[] inBuffer = new byte[22]; // this is the number of chars on each line from the Arduino (including /r/n)


InteractiveMap map;

ZoomButton out = new ZoomButton(5,5,14,14,false);
ZoomButton in = new ZoomButton(22,5,14,14,true);
PanButton up = new PanButton(14,25,14,14,UP);
PanButton down = new PanButton(14,57,14,14,DOWN);
PanButton left = new PanButton(5,41,14,14,LEFT);
PanButton right = new PanButton(22,41,14,14,RIGHT);

//PFont letters;

Button[] buttons = { 
  in, out, up, down, left, right };

PFont font;

boolean gui = true;

final int burst = 32;
int count = 0;

void myDelay(int time) {
  try {
    Thread.sleep(time);
  } catch (InterruptedException e) { }
}

void setup() {

 //  size(300,600);
   // size(VIEW_SIZE_X, VIEW_SIZE_Y);
     size(640, 480, OPENGL);
  smooth();

//  myPort = new Serial(this, serialPort, 115200);
    myPort = new Serial(this, serialPort, 38400);

  String template = "http://{S}.mqcdn.com/tiles/1.0.0/osm/{Z}/{X}/{Y}.png";
  String[] subdomains = new String[] { "otile1", "otile2", "otile3", "otile4" }; // optional
  map = new InteractiveMap(this, new TemplatedMapProvider(template, subdomains));

  map.setCenterZoom(new Location(39.425, -77.461), 17);
  //  map.setCenterZoom(new Location(newX, -newY), 17); 


  font = createFont("Helvetica", 12);


myPort.clear();

}

 float decodeFloat(String inString) {
  byte [] inData = new byte[4];

  if(inString.length() == 8) {
    inData[0] = (byte) unhex(inString.substring(0, 2));
    inData[1] = (byte) unhex(inString.substring(2, 4));
    inData[2] = (byte) unhex(inString.substring(4, 6));
    inData[3] = (byte) unhex(inString.substring(6, 8));
  } 


  int intbits = (inData[3] << 24) | ((inData[2] & 0xff) << 16) | ((inData[1] & 0xff) << 8) | (inData[0] & 0xff);
  return Float.intBitsToFloat(intbits);
}

void serialEvent(Serial pp) {
  if(pp.available() >= 18) {
    String inputString = pp.readStringUntil('\n');
    //print(inputString);
    if (inputString != null && inputString.length() > 0) {
      String [] inputStringArr = split(inputString, ",");
      if(inputStringArr.length >= 5) { // q1,q2,q3,q4,\r\n so we have 5 elements
        q[0] = decodeFloat(inputStringArr[0]);
        q[1] = decodeFloat(inputStringArr[1]);
        q[2] = decodeFloat(inputStringArr[2]);
        q[3] = decodeFloat(inputStringArr[3]);
      }
    }
    count = count + 1;
    if(burst == count) { // ask more data when burst completed
      pp.write("q" + char(burst));
      count = 0;
    }
  } }


void draw() {
 // background(0);

float newX = q[0] / 10000000;
float newY = q[1] / 10000000;


      println((float)newX + " " + (float)newY);
  println();

  map.draw();
 map.mouseDragged();
  if (gui) {
    textFont(font, 12);

  map.setCenterZoom(new Location(newX, newY), 17);
  Location location = map.pointLocation(newX, -newY);


      // draw the mouse location, bottom left:
    fill(0);
    noStroke();
    rect(5, height-5-g.textSize, textWidth("mouse: " + location), g.textSize+textDescent());
    fill(255,255,0);
    textAlign(LEFT, BOTTOM);
    text("mouse: " + location, 5, height-5);

    // grab the center
   // location = map.pointLocation(newX, newY);
    location = map.pointLocation(width/2, height/2);

    // draw the center location, bottom right:
    fill(0);
    noStroke();
    float rw = textWidth("map: " + location);
    rect(width-5-rw, height-5-g.textSize, rw, g.textSize+textDescent());
    fill(255,255,0);
    textAlign(RIGHT, BOTTOM);
    text("map: " + location, width-5, height-5);

   // location = new Location(39.425, -77.461); //(51.500, -0.126);
      location = new Location(newX, -newY); //(51.500, -0.126);

    Point2f p = map.locationPoint(location);

    fill(0,255,128);
    stroke(255,255,0);
    ellipse(p.x, p.y, 10, 10);
  }

}



void mouseDragged() {
  boolean hand = true;
  if (gui) {
    for (int i = 0; i < buttons.length; i++) {
      hand = hand ||  buttons[i].mouseOver();
     // if (!hand) break;
    }
  }
  if (hand) {
    map.mouseDragged(); 
  }
}

Answers

  • edited June 2014

    I edited above

  • This is the Button class that's part of the modestmaps example:

        class Button {
    
          float x, y, w, h;
    
    
          Button(float x, float y, float w, float h) {
            this.x = x;
            this.y = y;
            this.w = w;
            this.h = h;
          } 
    
          boolean mouseOver() {
            return (mouseX > x && mouseX < x + w && mouseY > y && mouseY < y + h);
          //   return (newX > x && newX < x + w && newY > y && newY < y + h);
    
          }
    
          void draw() {
            stroke(80);
            fill(mouseOver() ? 255 : 220);
            rect(x,y,w,h); 
          }
    
        }
    
        class ZoomButton extends Button {
    
          boolean in = false;
    
          ZoomButton(float x, float y, float w, float h, boolean in) {
            super(x, y, w, h);
            this.in = in;
          }
    
          void draw() {
            super.draw();
            stroke(0);
            line(x+3,y+h/2,x+w-3,y+h/2);
            if (in) {
              line(x+w/2,y+3,x+w/2,y+h-3);
            }
          }
    
        }
    
        class PanButton extends Button {
    
          int dir = UP;
    
          PanButton(float x, float y, float w, float h, int dir) {
            super(x, y, w, h);
            this.dir = dir;
          }
    
          void draw() {
            super.draw();
            stroke(0);
            switch(dir) {
              case UP:
                line(x+w/2,y+3,x+w/2,y+h-3);
                line(x-3+w/2,y+6,x+w/2,y+3);
                line(x+3+w/2,y+6,x+w/2,y+3);
                break;
              case DOWN:
                line(x+w/2,y+3,x+w/2,y+h-3);
                line(x-3+w/2,y+h-6,x+w/2,y+h-3);
                line(x+3+w/2,y+h-6,x+w/2,y+h-3);
                break;
              case LEFT:
                line(x+3,y+h/2,x+w-3,y+h/2);
                line(x+3,y+h/2,x+6,y-3+h/2);
                line(x+3,y+h/2,x+6,y+3+h/2);
                break;
              case RIGHT:
                line(x+3,y+h/2,x+w-3,y+h/2);
                line(x+w-3,y+h/2,x+w-6,y-3+h/2);
                line(x+w-3,y+h/2,x+w-6,y+3+h/2);
                break;
            }
          }
    
        }
    
  • edited June 2014

    Oh boy, I moved one statement from setup() into the loop and now it responds. It's too sensitive but it moves a map. That's amazing to me.

    I put this line in the draw() :

    `map.setCenterZoom(new Location(newX, newY), 4);

    Here's a link to the movie, I replaced the first one. This one is easier to watch and not very long. It just shows that it's basically working.

    I need to tame it if I expect it to work with GPS.

  • edited June 2014

    It's working with GPS now. I just made sure the lat and lon came in as an [4] array and the existing string/array decoder thing didn't know the difference. Now when it has a fix, the map moves with subtle changes in GPS position.

    It won't change the location of the "dot" though. That is stuck wherever it is at start for some reason.

    This all started as a question of how to move the map and now the question is:

    How do I get it to use offline maps and how do I get those maps?

  • I found an application that works with open street maps. It actually does what I'm trying to do in processing. That's fine, but once I have a database of maps it'd be nice to be able to access them with this program I'm trying to build.

    I see something in the modestmaps that will use openstreetmaps whether it needs to be online I don't know.

  • Not sure about ModestMaps, but Unfolding Maps (which used ModestMaps as its base in the beginning) allows using offline maps via TileMill.

    And you can find an example of an animated map using GPX data in the CenteredTrackApp

  • Thanks a lot for that!

    My crude little hack needs more methods() and Unfolding looks like what I need. TileMill looks powerful.

  • edited June 2014

    I found a java program that's called mobile atlas that looks like it will do what I need as far as archiving maps and creating a database in MBtiles. One of the Unfolding examples uses an offline MBtile. Maybe I can do something with that.

  • None of that is going to work. the mobile atlas is a very nice looking tool that doesn't do what I want. I used it to try to make a MBtile file and it starts to load in processing and just sits there with a grey window. So far GMapCatcher works great. It's written in python so maybe I could use the python mode in processing and borrow parts.

  • I sure know how to complicate in order to simplicate. I installed the Python mode and started playing with the GmapCatcher code. Python is a whole different animal, but I see a lot of potential. I can eliminate the Arduino and read my GPS rx directly through the serial port. I can also get and store tiles. I'm working on reading the GPS, get the map, store the map, and use processing to display it and move it and show a pipper and navigation info.

  • edited June 2014

    None of that worked very well, yet. I got python to put my tiles on localhost and when I try to get a map using Unfolding, the terminal window shows the request but Processing isn't getting any tiles.

    The question is how do I set up Unfolding to get tiles from localhost?

    I borrowed some code that extends a class for Mapbox:

    class streamProvider extends MapBox.MapBoxProvider {
      String hostname, mapname;
    
      streamProvider(String _hostname, String _mapname){
        hostname = _hostname;
        mapname = _mapname;
      }
    
      public String[] getTileUrls(Coordinate coordinate) {
        String url = "http://"+hostname+":8000/tiles"+mapname+"/"
          + getZoomString(coordinate) + ".png";
        return new String[] { 
          url
        };
      }
    }  
    
  • edited June 2014

    Here's the little python program that serves up the /tiles:

            import sys
            import BaseHTTPServer
            from SimpleHTTPServer import SimpleHTTPRequestHandler
    
    
            HandlerClass = SimpleHTTPRequestHandler
            ServerClass  = BaseHTTPServer.HTTPServer
            Protocol     = "HTTP/1.0"
    
            if sys.argv[1:]:
                port = int(sys.argv[1])
            else:
                port = 8000
            server_address = ('127.0.0.1', port)
    
            HandlerClass.protocol_version = Protocol
            httpd = ServerClass(server_address, HandlerClass)
    
            sa = httpd.socket.getsockname()
            print "Serving HTTP on", sa[0], "port", sa[1], "..."
            httpd.serve_forever()
    

    It'll act as a server on localhost in whatever directory. It works and Unfolding is requesting .png files but I still can't get it to display as a map.

  • I played around with what exactly is unfolding requesting and I saw one problem was it was requesting a space in the url. That makes sense that the file server wouldn't understand that so I changed it. The problem now is it still isn't giving up the files to unfolding. Here's what the python shell shows:

    Stream

    The request looks ok to me and I know the png's are there. What do I need to do?

  • edited June 2014

    Alright, I checked and the directory was read only. I changed that and it kinda works. I get a map but the tiles are in the wrong order maybe. The US is split with the Southern portion where the Northern portion should be. At least I've got something coming in and can start working with the Unfolding features.

    I figured out why the map order is all screwy and it's because I'm using MapBox.MapBoxProvider and in the comments it says the coordinate order is opposite to OSM and my repository is OSM. I'll just have to mess with that.

  • Well, that's just typical. I had the @#$%* thing working and then I go to get some coffee and come back and it's in the middle of shutting down. An hour later, I'm able to log in again and now it won't work.

    All I wanted to do was access OSM maps without being on the #$%&^ internet and then play with the GPS as a way to display a map. I saw it work for about 20 minutes and even then it was half broken because it wasn't the right order for coordinates.

  • edited June 2014

    It's the Devil. That's just plain evil. I had it working and then without warning it starts shutting down to apply an update. An hour later and I can log in again and what took me a week to get working, now doesn't.

  • Wow, went nuts.

    I got it working without the python http server. I fixed the tile order and went back to modestmaps for the buttons. I might try using Unfolding and see if I can add buttons. The GPS is still being parsed on the Arduino at the moment.

    One thing that makes using processing not so desireable is when it requests a tile that isn't available, the CPU usage goes to the limit. I don't want to burn the thing up.

  • Hi ! I was wondering if you finally made it to work. I am also trying for few days now to work with offline maps , with no luck at all . Any ideas you would like to share?

  • @HarryCodes:::

    i have used osm maps offline in android, putting them into the sd card; maps created with MOBAC. As for java i have done the same with JMapViewer + eclipse; normally JMapViewer uses http url, but it s very simple to modify that. As for modestmaps i think it s the same, you can add some "getTilesFromFiles()" class using the path to your maps as arg.

  • @akenaton: What types does JMapViewer support ? Is there any chance you can draw on that map in real time?

  • @atomtm::: not sure to understand what do you mean by "types"... but jmapViewer is quite the same (more sophisticated) than modestmaps...

  • @akenaton : Does it support .shp files , .osm , .mbtiles?

  • I moved on somewhat better things. I adapted ModestMaps to work on Android. I had to comment out one line in the source and it compiled. I don't know how important or even what that one line even did but Android didn't need it.

Sign In or Register to comment.