Loading...
Logo
Processing Forum
All,
 
I'd like to create an interactive .svg map (for instance, a map of the US where you can mouse over states to display a tooltip with data pertaining to that state). Can this be done with processing.js? I understand that with native Processing you'd probably use something like geomerative, but my understanding is that geomerative doesn't work with pjs. Any ideas here? Thanks!

Replies(18)

You know, you can make SVG files with JavaScript inside...
I found back a couple of old examples on my hard disk:
http://pastebin.com/JR5wsVAn
http://pastebin.com/UPs4q2nB

Also a Mozilla example: https://developer.mozilla.org/samples/svg/swarm-of-motes.xhtml explained in https://developer.mozilla.org/en/SVG/Namespaces_Crash_Course/Example
This one has JS external to SVG.
Thanks phi.lo. I was hoping to do something like this completely within processing/pjs but it doesn't look like that's the case? Looks like for something like this I'd be better off using Raphael or something similar.
 
Maybe let me reframe the question: philo, if you were tasked with creating a web-friendly interactive map of the US that had to run in the browser without reliance on a plug-in, how would you do it? Not looking for a step-by-step by any means, but rather a run-down of the tools/libraries you'd use and how you'd get them talking to each other. Thanks again!
 
Chris
I would just search the Web for a pure JavaScript library for manipulating such schematic map. I believe there are a number of them (sometime as chart libraries), but I never used them.
I think it´s possible only with processingjs. You don´t need any library to load a svg on processingjs:        
  http://processingjs.org/reference/loadShape_/
After that you can work with the children of the svg so you can set the colours asigned by parsing some data (I´m supposing you want to make a choropleth map):
  http://processing.org/learning/basics/getchild.html
And afterwards you can solve the hovering by color picking (easiest way).
That´s the way I´d do it. But I haven´t tried yet, so probably there will be tons of problems aside. As always... ;-)

Ale · 60rpm.tv/i
Thanks for the responses. 60rpm -- I hadn't thought of color picking, will have to look into that. Question though: would that cause issues if say, two states had the same value and hence the same color?
You´re right, of course... a way I can imagine of solve that easily, would be to have stored the coordinates of the bounding boxes of the conflictive cases, so you could check both color and mouse coordinates then. I mean, first you check color and if this one is a conflictive color, then you check mouse coordinates.
Another way would be to have two maps, one invisible and one visible... you visualize the data on the visible one but you get colors from the invisible one (a picture buffered in a PGraphics), but maybe this is expensive. All depends!  :-)
And I´m sure there are more creative solutions to solve easily this interesting problem... Of course there are really and serious good algorithms to achieve this goals, but they are quite tough and they might not be what you are looking for.

Ale · 60rpm.tv/i
I don't think you need color picking with multiple colors. Black and white do it.

Instead of drawing the whole map as one SVG shape, you cycle through the SVG children (the states). In the loop, you

- draw the state with a black color to a temporary buffer PImage (which has same width and height as the canvas)
- check in the buffer PImage whether the color under the mouse pointer is black or white and set a flag accordingly
- store with the flag the index of the state that was selected (i.e. the loop index or some other index)
- then draw the state according to the flag, get the right info with the help of the index and display the tooltip

And yes, that can be made with PJS.
A tutorial on color picking, maybe it´s useful:
http://wiki.processing.org/w/Picking_with_a_color_buffer

Ale · 60rpm.tv/i
Thank you for the thoughtful responses. I apologize for resurrecting a dead thread but I've just now had the time to start implementing this.
 
Picking a color under the mouse seems pretty straightforward -- color mouseColor = get ( mouseX , mouseY ); But I'm having a hard time with the buffer, partially because I don't fully understand the concept. The tutorial was a little mystifying -- the use of color is not what I'm used to (white is -1? huh?), and I can't figure out how to apply what's going on there to my own code.
 
My code is below -- it's basically the .svg example from 321 of Visualizing Data, updated to use PShape instead of the deprecated candy library. Any pointers you can give to set me in the right direction would be most appreciated!
 
 
PShape USMap;
PShape state;
Table data; //using table.pde
float dataMin = -7;
float dataMax = 11;
void setup() {
  size(700, 500);
  USMap = loadShape("usmap.svg"); // from data folder
  data = new Table("random.tsv"); // from data folder
  smooth();
}
void draw() {
  background(255);
  noStroke();
  USMap.disableStyle();
  int rowCount = data.getRowCount();
  for (int row = 0; row < rowCount; row++) {
    String abbrev = data.getRowName(row);
    state = USMap.getChild(abbrev);
    if (state == null) {
      println("No state found for" + abbrev);
    }
    else {
      float value = data.getFloat1(row, 1);
      if (value >= 0) {
        float amt = norm(value, 0, dataMax);
        color c = lerpColor(#FFFFFF, #221177, amt);
        fill(c);
      }
      else {
        float amt = norm(value, 0, dataMin);
        color c = lerpColor(#FFFFFF, #992211, amt);
        fill(c);
      }
      stroke(255);
      shape(state, 0, 0);
    }
  }
}
Update -- I'm getting close on this. I've got it to where I've got a buffer image with a unique color for each state and I'm able to grab the color using get(). However, I can't figure out how to use the picked color to change the attributes in the svg child object. As you can see in the code I'm trying to change the stroke color for moused-over items, and to do so I'm checking whether the index (in this case, the row count) is equal to the picked color, but it doesn't seem to be working. Pretty sure I'm mucking something up with the loop here but can't put my finger on it...
 
 
PShape USMap;
PShape state;
PGraphics buffer;
Table data;
color c, buffercolor, mousecolor;
float dataMin = -7;
float dataMax = 11;
void setup() {
  size(700, 500);
  buffer = createGraphics(width, height, JAVA2D);
  USMap = loadShape("usmap.svg");
  data = new Table("random.tsv");
  smooth();
}
void draw() {
  background(255);
  noStroke();
  USMap.disableStyle();
  int rowCount = data.getRowCount();
  for (int row = 0; row < rowCount; row++) {
    String abbrev = data.getRowName(row);
    state = USMap.getChild(abbrev);
   
    buffer.beginDraw();
    buffercolor = row;
    noStroke();
    buffer.fill(buffercolor);
    buffer.shape(state, 0, 0);
    buffer.endDraw();
    mousecolor = buffer.get(mouseX, mouseY);
   
    if (state == null) {
      println("No state found for" + abbrev);
    }
    else {
      float value = data.getFloat1(row, 1);
      if (value >= 0) {
        float amt = norm(value, 0, dataMax);
        c = lerpColor(#FFFFFF, #221177, amt);
        fill(c);
      }
      else {
        float amt = norm(value, 0, dataMin);
        c = lerpColor(#FFFFFF, #992211, amt);
        fill(c);
      }
     
      fill(c);
      if (row == mousecolor) {
        stroke(0);
      }
      else {
        stroke(255);
      }
      shape(state, 0, 0);
    }
  }
println(mousecolor);
  fill(mousecolor);
  rect(width - 50, height - 50, 10, 10);
}
Got it! Sorry for the multiple posts, but I finally got this to work and figured it may be useful to someone else. The problem with the code immediately above is that the get() was returning a ridiculous negative value (not sure why -- any ideas?). Since this value was never equal to the row index, the attributes never changed.

What I did below was run red(mousecolor) to get the red value for mousecolor. This corresponds neatly to the row index, and voila -- problem solved!
 
The return value on get() really has me flummoxed though -- what is that enormous number that's getting passed through?
 
Thanks again -- going to see about getting this to work in a pjs context now...
 
 
 
PShape USMap;
PShape state;
PGraphics buffer;
boolean flag;
Table data;
float redint;
color c;
color buffercolor, mousecolor;
float dataMin = -7;
float dataMax = 11;
void setup() {
  size(700, 500);
  buffer = createGraphics(width, height, JAVA2D);
  USMap = loadShape("usmap.svg");
  data = new Table("random.tsv");
  smooth();
  flag = false;
}
void draw() {
  background(255);
  noStroke();
  USMap.disableStyle();
  int rowCount = data.getRowCount();
  for (int row = 0; row < rowCount; row++) {
    String abbrev = data.getRowName(row);
    state = USMap.getChild(abbrev);
   
    buffer.beginDraw();
    buffer.background(255);
    buffercolor = row;
    buffer.noStroke();
    buffer.fill(buffercolor);
    buffer.shape(state, 0, 0);
    buffer.endDraw();
    mousecolor = buffer.get(mouseX, mouseY);
    redint = red(mousecolor);
   
    if (state == null) {
      println("No state found for" + abbrev);
    }
    else {
      float value = data.getFloat1(row, 1);
      if (value >= 0) {
        float amt = norm(value, 0, dataMax);
        c = lerpColor(#FFFFFF, #221177, amt);
        fill(c);
      }
      else {
        float amt = norm(value, 0, dataMin);
        c = lerpColor(#FFFFFF, #992211, amt);
        fill(c);
      }
     
      stroke(255);
      fill(c);
     if (redint == row) {
    fill(100,100,100);
     }
      shape(state, 0, 0);
    }
  }
println(mousecolor);
println(redint);
  noLoop();
}
void mouseMoved() {
  loop();
}
Colors are ints. Color() method is only an intuitive way of handling the color space. It assigns the color specified by color space components to its proper int index.
i.e. in (RGB,255) color space--default--:
      color(255,255,255) = -1  [white] 
      color(0,0,0) = 0 [black] 
      color(255,0,0) = -65536 [red]
etc!

Ale · http://60rpm.tv/i
Got it -- thanks!
 
@  cingraham, this is great.  I think I shall attempt to duplicate your work... very helpful.

Your last set of code... what is the Table class?  And what form does the SVG take?  I'd really like to see your completed sketch.

Thanks for blazing the trail.
Glad you found it useful!

Table is table.pde ( http://benfry.com/writing/map/Table.pde) -- a table parser developed by Ben Fry. The version I used has some slight modifications to make it processing.js-friendly.

The svg is just an svg map of the US downloaded from Wikipedia.

One unfortunate thing I later discovered is that this type of color picking currently does *not* work in processing.js -- there's currently an issue with drawing offscreen PGraphics buffers. It's on their fix list, but last I heard may not be addressed until pjs version 1.5.

So unfortunately I don't have anything to show you just yet!
ok!  well, it's nice to know we're on the razor's edge.  :)  If I make any progress, I'll post back here.
Update! With processing.js 1.4 (out today!) color-picking via an offscreen buffer works beautifully for svg's. I just ran the code above via pjs 1.4 and it worked fine with no modifications. Decent performance in Firefox, lightening-fast in Chrome:)

http://processingjs.org/blog/2012/07/31/processing-js-v1-4-0-released.html