Question

I'm building a site that has an svg map of the United States using the very easy usmap jquery plugin which uses Raphael. I have a click event that fires if you click an individual state.

On that occasion, I go to the same US States object to render a single state. The problem is that the paths for each state in the list are relative to each other as rendered in the entire US map of states.

But my requirements are that I need to render each state about the same size as each other when I'm viewing a single state.

I know I can put a multiplier on the path parameters and the overall state will increase/decrease, but I'm looking for a method to determine this multiplier dynamically.

I also tried to use setViewBox, hoping it would increase/decrease the <path> to fill the area. But it didn't work, as far as I could tell.

So, either I need to make the <path> fill the space or I need to figure out a multiplier to apply to scale the <path>

// state coords pulled from All states svg file
var statePolygon = "M 93.573239,6.3617734 L 97.938071,7.8167177 L 107.6377,10.564946 L 116.2057,12.504871 L 136.2516,18.162988 L 159.20739,23.821104 L 174.36801,27.215777 L 173.36373,31.099829 L 169.27051,44.909503 L 164.81238,65.714155 L 161.63584,81.854036 L 161.28429,91.232806 L 148.10315,87.33877 L 132.53264,83.955591 L 118.86585,84.551329 L 117.28528,83.01913 L 111.95881,84.916253 L 107.9821,84.665645 L 105.2606,82.904814 L 103.68223,83.430208 L 99.476903,83.201576 L 97.601755,81.829846 L 92.824862,80.093194 L 91.382778,79.886558 L 86.397035,78.560984 L 84.614222,80.069004 L 78.922841,79.726077 L 74.101997,75.931831 L 74.30643,75.131651 L 74.374575,67.197996 L 72.248826,63.31142 L 68.133618,62.57938 L 67.768708,60.225014 L 65.2543,59.597968 L 62.372763,59.063086 L 60.594498,60.033049 L 58.331251,57.123161 L 58.654572,54.213272 L 61.4028,53.889951 L 63.019405,49.84844 L 60.432837,48.716816 L 60.594498,44.998625 L 64.959331,44.351984 L 62.211103,41.603756 L 60.756158,34.490695 L 61.4028,31.580807 L 61.4028,23.659444 L 59.624535,20.426234 L 61.887782,11.049927 L 63.989368,11.534908 L 66.414275,14.444797 L 69.162503,17.031364 L 72.395712,18.97129 L 76.922205,21.072876 L 79.993756,21.719518 L 82.903645,23.174462 L 86.298518,24.144425 L 88.561764,23.982765 L 88.561764,21.557857 L 89.855048,20.426234 L 91.956634,19.13295 L 92.279955,20.264574 L 92.603276,22.042839 L 90.340029,22.52782 L 90.016708,24.629406 L 91.794974,26.084351 L 92.926597,28.509258 L 93.573239,30.449183 L 95.028183,30.287523 L 95.189843,28.994239 L 94.219881,27.700955 L 93.734899,24.467746 L 94.543201,22.689481 L 93.89656,21.234537 L 93.89656,18.97129 L 95.674825,15.41476 L 94.543201,12.828192 L 92.118294,7.9783781 L 92.441615,7.1700758 z M 84.116548,12.340738 L 86.137312,12.179078 L 86.622294,13.553197 L 88.158073,11.936582 L 90.502155,11.936582 L 91.310458,13.472361 L 89.774678,15.169801 L 90.42133,15.978114 L 89.693853,17.998875 L 88.319734,18.403021 C 88.319734,18.403021 87.430596,18.483857 87.430596,18.160536 C 87.430596,17.837215 88.885551,15.573958 88.885551,15.573958 L 87.188111,15.008141 L 86.86479,16.463095 L 86.137312,17.109737 L 84.60153,14.84648 z";
var width = 175, 
    height = 125,
    paper = Raphael(document.getElementById("statemap"), 350, 250);

paper.setViewBox(0, 0, width, height, true);
//  paper.canvas.setAttribute('preserveAspectRatio', true);
paper.path(statePolygon);

Anyone have any ideas?

Update

So, I figured out (thanks, Jordan) that you can get a "size" dimension by capturing the difference between all of the X coord (can be done with y, if you'd rather.) By calculating the diff between the lowest X and the highest X you can get a size.

If you then figure out what a 'normal' size would be, you can then determine a multiplier to apply to each state as a fraction of the state's size relative to the 'normal' one.

In my example, the diff in Kansas is 128. CT is 29, so CT needs a multiplier of 4.41 to bring it up to the relative size of Kansas. TX is 242, so it needs a 0.53 multiplier to bring it down.

This normalizes the sizes of the states. If you then, using Raphael, need to increase/decrease this, you can use the transform function:

p = paper.path(statePolygonNew);
var scale = 1.5;
p.transform("t" + scale*52 + "," + scale*32 + " s" + scale + "");

Thanks, Scott

Était-ce utile?

La solution

There are a number of different ways of approaching this. Here are two of them.

Match the viewbox to the existing path

You used this method incorrectly when you were first seeking a solution. The viewbox is essentially a mechanism for dynamically scaling the way that coordinates in the SVG are translated into coordinates on a screen. By setting the viewbox to the dimensions of the svg in screen coordinates, you are effectively telling it to do nothing -- that's what it does by default.

Instead, try setting the viewbox to the bounding box of the target path:

var statePathString = "...";  // your path here
var statePath = paper.path( statePathString ).attr( { /* your attributes */ } );
var bbox = statePath.getBBox();
paper.setViewBox( bbox.x, bbox.y, bbox.width, bbox.height, true );

This instantiates the path for the state, calculates its dimensions, and zooms the viewbox in to look at just that range of the SVG's coordinate systems. You may need to add additional logic to preserve aspect ratios and add margin, but this is definitely the easiest approach.

Transform the path to fit your existing viewbox

If you do opt to avoid viewbox manipulation -- there are a few valid reasons -- then you can adjust the path to match the default viewing rectangle using a handful of Raphael's built-in utility functions. Here's the approach I'd use, given existing variables width and height reflecting the dimensions of your paper:

var bbox = Raphael.pathBBox( statePathString );  // Handy function to retrieve a bounding box without instantiating a path

//  Calculate larger of the two dimensional quotients
var scale = Math.max( bbox.width / width, bbox.height / height );

//  Finally, transform the path such that it is translated to have its origin at 0,0 and scaled in such a way that it will fill the SVG
var transformedPathString = Raphael.transformPath( statePathString,
                                                   [ "T", 0 - bbox.x, 0 - bbox.y,   /* Shift up and left so upper-left extent corresponds to 0,0 */
                                                     "S", 1.0 / scale, 1.0 / scale, 0, 0 ] );

At this point you can do whatever you'd like to do with transformedPathString -- presumably, pass it to paper.path and style it up.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top