d3.js extract coordinates of SVG path elements - javascript

After spending quite some time on this problem without any success, I decided to post my (first!) question here.
For a project I used topojson to plot part of a map. In my SVG I have some groups containing some points (which I placed using the geojson.io tool).
The map looks like this: plotted map
The structure of the code looks like this:
<svg width='1300' height='900'>
<g class="gate" id="D5">
<path d="M645.3426080089994,434.9821086442098m0,4.5a4.5,4.5 0 1,1 0,-9a4.5,4.5 0 1,1 0,9z"></path>
</g>
<g class='gate' id='d4'>
<path d="M605.3552137160441,383.2755208062008m0,4.5a4.5,4.5 0 1,1 0,-9a4.5,4.5 0 1,1 0,9z"></path>
</g>
</svg>
What I'd like to do is append some SVG based on some condition on the exact location of these points. Now while I know how to append SVG's, I'm having trouble extracting the coordinates of the point on the map. The SVG that I would like to append must be placed upon the points in the map. However, when for example I append a circle to the group, the coordinates that I enter are not considered relative to the point, but rather absolute to the whole SVG area. The circle ends up in the upper left corner instead of on the actual point.
I attempted to extract the coordinates of each point in order to use this data for plotting, but I have not yet succeeded. I managed to get the node by using:
d3.selectAll('#D5').select('path').node()
Which returns:
<path d="M605.3552137160441,383.2755208062008m0,4.5a4.5,4.5 0 1,1 0,-9a4.5,4.5 0 1,1 0,9z"></path>
When I look inside the path object, I see that deep inside the __ data__ element there are some coordinates, but I'm not able to extract these.
This issue wouldn't be a problem if I defined the coordinates for the point at start. However, due to the fact that this is topojson, the whole image is just a composition of path elements.
How can I plot a SVG on the points without explicitly defining coordinates beforehand? If any more information is required, please let me know.
Thanks!

What you are looking for is the getBBox() function
d3.selectAll('#D5').select('path').node().getBBox()
// { x: 640.8425903320312, y: 430.48211669921875, width: 9, height: 9 }
See https://jsfiddle.net/7zahj9gt/

Related

How to get bounding coordinates from an SVG path

I'm working on a project that requires bounding draggable elements inside complex shapes, defined ideally by SVG Path elements, using Javascript.
I'm open to other ways of defining the bounds but SVG would tie in better with the source material.
I've tried using pathSegList however this returns undefined, and I've read it's deprecated.
How would I go about getting a list of coordinates from an SVG path element, which I could translate to X/Y coordinates for Javascript?
You can use the .getBBox() method on the path element. Take a look at the snippet:
console.log(document.getElementById('mypath').getBBox());
<svg width="300" height="100" viewBox="0 0 300 100" style="background:#efefef">
<path id="mypath" d="M20,20 L40,20 40,40, 20,40 Z" fill="red" />
</svg>

Highcharts Grid Line Width (or Color) - Single Line

I have the following Highcharts (http://www.highcharts.com) scatter chart. Note that the axes start at -10 and stop at 10, with 0 in the middle. I want each 0 line to be a different width or color from all the others. Should be easy but I can't find in the API documentation how to do. All I can find is how to change properties for all lines. Has anyone done this or have suggestions?
Outside of the API I looked into using Javascript (specifically jQuery) to update the element's style but the only specific part (d) I could easily use to find which line to update isn't always the same. The element looks like.....
<path stroke-width="10" opacity="1" stroke="#D8D8D8" d="M 293.5 72 L 293.5 502" fill="none"></path>
Thanks.
You can implement plotLines. Check this out.
In yAxis, I have set a green line at value of 70.
In xAxis, I have set a blue line at value of 170.
You may play around with the color and width as well.

SVG pie chart pull text to top

I have a SVG pie chart that is being auto generated by code. I want to ensure the text always shows up, however it is getting overlapped by color fills in the pie chart. I've tried using render-order to fix this to no avail. What can I do to get the equivalent of z-index in a SVG?
Here is the example code. Copy/paste and you'll see the problem.
<svg width="180" height="180"><g transform="translate(90,90)"><g class="slice"><path fill="#D0D0D0" d="M-39.049536520580226,81.08719811121772A90,90 0 0,1 -87.74351209636413,20.026884056068305L0,0Z"></path><text transform="translate(-35.18241671106134,28.05704108364301)" text-anchor="middle" render-order="109">Black - 14%</text></g><g class="slice"><path fill="#CEECC6" d="M-87.74351209636413,20.026884056068305A90,90 0 0,1 -70.36483342212269,-56.114082167286L0,0Z"></path><text transform="translate(-40.871756048182064,-10.013442028034143)" text-anchor="middle" render-order="300">Green - 14%</text></g><g class="slice"><path fill="#FFA8A8" d="M-70.36483342212269,-56.114082167286A90,90 0 0,1 -1.6532185776602093e-14,-90L0,0Z"></path><text transform="translate(-19.524768260290124,-40.54359905560886)" text-anchor="middle" render-order="109">Red - 14%</text></g><g class="slice"><path fill="#FFE8A1" d="M5.510728592200698e-15,-90A90,90 0 1,1 -39.049536520580226,81.08719811121772L0,0Z"></path><text transform="translate(43.871756048182064,10.013442028034147)" text-anchor="middle" render-order="109">Yellow - 57%</text></g></g></svg>
There's no z-index or render-order in SVG currently, although this is planned for the next version of the specification (SVG 2) which is currently being written by w3c. It uses painter's model i.e. whatever you put in the file last is drawn on top like a painter always paints on top of what's gone before.
If you want the text to be on top then you must add it to the SVG file after anything that it may overlap.

Fill Svg Path with rects

i am in a situation where i need to Fill Svg path with different rects. here 'different' means each rect will be a separate svg element, and i can access it using id i.e document.getElementById('rect_');
I cannot use 'Patterns' because they fill path but i can't access each element separately .
let say i have path :-
<path d="M 10 10 L 50 10 80 125Z" stroke="red" fill='none' />
Now i need to fill this path with rects. Rect specs are:-
<rect x=0 y=0 width=5 height= 5 stroke="red" />
*I can convert path into points (x y cordinates) but unable to make logic to create rects inside that path.
Explanation:-
Let me explain the whole senerio , i am developing an application in svg in which user can draw his custom path (just like painting) , once he finishes his drawing, path will be closed and then i need to fill that path with 'n' number of 'rects' . now i need to determine how many rects will be drawn and at what place . Rects must be inside that path area.
I need help on this,
thanks.

Dilate and erode SVG shapes using Javascript

EDIT:
I finally found a way to erode and dilate polygons (offsetting) so that new geometry is created using Clipper library:
https://sourceforge.net/projects/jsclipper/
Live demo of Javascript Clipper:
http://jsclipper.sourceforge.net/5.0.2.1/main_demo.html
The Clipper can only handle polygons or multi-polygons (eg. polygons with holes), so for it to work with other graphical objects of SVG format, they have to be converted to straight lines. At least paths are rather easy to convert to lines using path.getTotalLength() and path.getPointAtLength() (http://whaticode.com/2012/02/01/converting-svg-paths-to-polygons/).
The other possibility is use this like technique (that does not create new geometry):
https://stackoverflow.com/a/12723835/1691517
Is there any way to erode and dilate shapes in SVG via Javascript?
I have the following SVG example:
http://jsfiddle.net/timo2012/2S4Kt/1/
There are three shapes, blue is original, green is eroded (thinned) and red is dilated (bolded). They are made in Illustrator.
I have tested erode and dilate filters, but the effect is not so good:
https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/examples/feMorphology.svg
After few hours searching over internet, I have found only examples about bitmap image eroding and dilating, but nothing about vector shapes.
I have succeeded in dilating and eroding SVG polygons using Shapely ( http://toblerity.github.com/shapely/manual.html ) in Python by sending path points via Ajax call to PHP script which makes system() call to Python script, but this method is slow and requires server to do the work that could be done client side.
This is my code for dilating and eroding in Python (as you see it is quite short):
#!/usr/bin/python26
from shapely.geometry import Polygon
from shapely.geometry import MultiPolygon
import sys
if len(sys.argv)>2:
inset=eval(sys.argv[1])
coords=eval(sys.argv[2])
else:
sys.exit()
bowtie = Polygon(coords)
clean = bowtie.buffer(inset)
clean = clean.simplify(1, preserve_topology=False)
if clean.length>0:
if clean.geom_type=="MultiPolygon":
for n in range(0, len(clean)):
print list(clean[n].exterior.coords)
#print "\n"
elif clean.geom_type=="Polygon":
print list(clean.exterior.coords)
Also find this document, which tries to define dilate and erode in mathematical terms:
http://en.wikipedia.org/wiki/Mathematical_morphology
There is a sentence "The basic idea in binary morphology is to probe an image with a simple, pre-defined shape, drawing conclusions on how this shape fits or misses the shapes in the image. This simple "probe" is called structuring element, and is itself a binary image (i.e., a subset of the space or grid)."
I assume that this method could be used in morphing vector shapes, but how...
EDIT: One comment in a reply raised a possible issue of using filters instead of creating new geometry: if someone wants to add drag handles to polygon points, then drag handles may seem to be in wrong place. This can be acceptable, because then the impression is that the original path data is untouched, which is actually the case in filters, but - after further testing - it proved that the quality is a bigger problem. According to this and this SVG filter uses pixel representation of vector graphic object instead of path data itself, which leads to not so good looking results.
EDIT2: POSSIBLE WORKAROUND: One of the answers in this page led me to use variable-width strokes and mask to achieve a good looking workaround to this issue. I made a few tests and get implemented an Adobe Illustrator -like Offset Path Effect.
You can sort of get what you seem to be after by stroking with different stroke-widths in combination with clip-path or mask. Here's an example, some explanations of how it's constructed see here and here (arrow up or down to see some other slides on that example).
It doesn't give you new geometry though, just something that might look like new geometry.
Have you actually tested SVG's native filters? This looks close enough:
<svg width="612" height="792" viewBox="0 0 612 792" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="erode">
<feMorphology operator="erode" in="SourceGraphic" radius="12" />
</filter>
<filter id="dilate">
<feMorphology operator="dilate" in="SourceGraphic" radius="8" />
</filter>
<path id="original_path" d="M193.193,85c23.44,0.647,45.161,0.774,62,12c1.596,1.064,12,11.505,12,13
c0,2.941,8.191,5.669,3,12c-3.088,3.767-6.01-0.758-11-1c-19.56-0.948-33.241,12.296-33,34c0.163,14.698,8.114,24.492,4,41
c-1.408,5.649-6.571,15.857-10,21c-2.484,3.726-7.898,10.784-12,13c-4.115-11.677,2.686-27.29-6-35c-6.693-5.942-20.021-4.051-26,1
c-13.573,11.466-11.885,41.492-7,58c-5.8,1.772-18.938,7.685-23,12c-6.752-10.805-15.333-17.333-24-26c-3.307-3.307-9.371-12-15-12
c-16.772,0-13.963-15.741-13-28c1.283-16.324,1.727-28.24,4-42c1.276-7.72,8-16.411,8-23c0-7.416,15.945-29,23-29
c4.507,0,17.678-8.701,24-11C164.853,90.76,178.27,88.546,193.193,85"/>
</defs>
<use xlink:href="#original_path" fill="#f00" filter="url(#dilate)"></use>
<use xlink:href="#original_path" fill="blue"></use>
<use xlink:href="#original_path" fill="#1CFF00" filter="url(#erode)"></use>
</svg>
There is some clipping going on the dilate filter that can't seem to be resolved by increasing the filter region, but other than that it's pretty close to your illustrator rendering. Sure beats rendering server-side.
http://jsfiddle.net/5Qv5v/

Categories