how to use svg icons in openlayers 4 if collected from the svg file - svg

the problem is the following, have the svg file format:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px">
<symbol id="gear" viewBox="0 0 300 300" enable-background="new 0 0 300 300" xml:space="preserve">
<path fill="#inherit" stroke="#000000" d="..."/>
</symbol>
<symbol id="drill" viewBox="0 0 300 300" xml:space="preserve">
<path style="stroke:none; fill:#000000" d="..."/>
</symbol>
<svg>
do the following to make this file an icon and displayed on the map. The very icon collect:
SVG.create('img/iconsSvg.svg#gear','img/iconsSvg.svg#drill') it can be displayed in html
var SVG={
svgns:'http://www.w3.org/2000/svg',
xlink:'http://www.w3.org/1999/xlink',
create:function(){
let pathUse=arguments;
var svg=document.createElementNS(SVG.svgns, 'svg');
for (let i = 0; i < pathUse.length; i++) {
let use=document.createElementNS(SVG.svgns, 'use');
use.setAttributeNS(SVG.xlink, 'xlink:href', pathUse[i]);
svg.appendChild(use);
}
return svg;
}
}
in openlayers set the style of this:
function setStyleIcon(){
return new ol.style.Style({
image: new ol.style.Icon({
img:SVG.create('img/iconsSvg.svg#gear'),
imgSize:[30,30]
})
});
}
when it crashes the error: Argument 1 of CanvasRenderingContext2D.drawImage could not be converted to any of: HTMLImageElement, HTMLCanvasElement, HTMLVideoElement, ImageBitmap.
how to translate svg to canvas I do not know
but I can't put it on the map, thank you in advance for your help

Found a solution not very nice, the svg is inserted into a js file and work with it as with text, can someone come in handy:
Function set the style:
function setStyleSvg(type,color,scale){
var svg=svgIcon.getSvg([['location',color],[type,'rgb(100,100,100)',true]]);
var style=new ol.style.Style({
image: new ol.style.Icon( {
src:'data:image/svg+xml;utf8,'+svg,
scale: scale,
anchor: [0.5, 1],
anchorXUnits: 'fraction',
anchorYUnits: 'fraction',
})
});
return style;
};
collect svg (color need rgb/rgba format "for angularJS"):
app.factory('svgIcon',function(){
var size=[500,500];
var cssClass="unit";
var viewBox="0 0 2000 2000";
const closeTag='</svg>';
var insert='';
function icons(icon,color,label){
var out;
var offset=(label)?-250:0;
switch(icon){
case 'truck':out+=`<path viewBox="0 0 1024 538" transform="translate(448,`+(731+offset)+`)" fill="`+color+`" stroke="rgb(0,0,0)" stroke-width="10" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="1" d="...."/>
`;
break;
case 'drill':out+=`....`;
break;
case 'excavator':out+=`....`;
break;
}
return out;
}
this.setClass=(className)=>{
cssClass=className;
};
this.setViewBox=(set)=>{
viewBox=set;
};
this.setInsert=(set)=>{
insert=set;
};
this.getSvg=(arr)=>{//([[type,color,label],[type,color,label]....])
try {
var svg=`<svg width="500" height="500" viewBox="`+viewBox+`" `+insert+` class="`+cssClass+`" version="1.1" xmlns="http://www.w3.org/2000/svg">`;
for (var i = 0; i < arr.length; i++) {
svg+=icons(arr[i][0],arr[i][1],arr[i][2]);
}
svg+=closeTag;
return svg;
} catch (err) {
console.log('svg not create');
}
}
return this;});

Related

SVG - Draw scrolling issue with multiple instances

I am trying to create a page (in Wordpress), which is essentially a timeline. As the user scrolls to the next sections, I have a vertical line that "connects" to the next content section. After a LOT of trial and error, I was able to create a line that "draws" itself on scroll, and reverses when scrolling back up. My issue is, when I try to use the code again in the same page, it is already drawn, -- in other words, I *think there is an issue with the code not knowing that is is not supposed to trigger yet. I do not know enough about this to know why it is not working. ideally, I want each line to start drawing as the view-box/browser window is in view.
I have tried creating unique ID's, unique div's and ID's, etc. I originally thought it may be an issue with needing unique containers/ID's. Now, I am *thinking it might be because I do not know how to tell the "line" to not be visible until it is pulled into view.
Here is my pen:
// Get the id of the <path> element and the length of <path>
var triangle = document.getElementById("triangle");
var length = triangle.getTotalLength();
// The start position of the drawing
triangle.style.strokeDasharray = length;
// Hide the triangle by offsetting dash. Remove this line to show the triangle before scroll draw
triangle.style.strokeDashoffset = length;
// Find scroll percentage on scroll (using cross-browser properties), and offset dash same amount as percentage scrolled
window.addEventListener("scroll", myFunction);
function myFunction() {
var scrollpercent = (document.body.scrollTop + document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
var draw = length * scrollpercent;
// Reverse the drawing (when scrolling upwards)
triangle.style.strokeDashoffset = length - draw;
}
body {
height: 200vh;
}
#mySVG {
position: relative;
top: 15%;
left: 50%;
width: 50px;
height: 710px;
}
<svg id="mySVG" preserveAspectRatio="none" viewBox="0 0 4 100">
<path fill="none" stroke="#000000" stroke-width="1"
id="triangle" d="M 0 0 V 100 0"/>
</svg>
Whenever you need to manipulate multiple elements, you need to query these elements to get an array/list and then loop through all nodes in this array.
Usually it's a better approach to use class names to avoid non-unique IDs like so
let triangles = document.querySelectorAll(".triangle");
triangles.forEach( (triangle) => {
// do something
});
So add class names (or just replace id attributes) to your <path> elements and add a scroll EventListener
let triangles = document.querySelectorAll(".triangle");
// calculate path length and init
triangles.forEach((triangle) => {
let pathLength = triangle.getTotalLength();
triangle.setAttribute("stroke-dasharray", `0 ${pathLength}`);
});
// Find scroll percentage on scroll
window.addEventListener("scroll", (e) => {
drawLines(triangles);
});
function drawLines(triangle, pathLength) {
var scrollpercent =
(document.body.scrollTop + document.documentElement.scrollTop) /
(document.documentElement.scrollHeight -
document.documentElement.clientHeight);
triangles.forEach((triangle) => {
let pathLength = triangle.getAttribute("stroke-dasharray").split(" ")[1];
var dashLength = pathLength * scrollpercent;
triangle.setAttribute("stroke-dasharray", `${dashLength} ${pathLength}`);
});
}
body {
padding:0 5em;
height: 200vh;
margin:0;
}
svg {
position: relative;
top: 15%;
left: 50%;
width: 50px;
height: 710px;
}
path{
transition:0.4s 0.4s;
}
<svg preserveAspectRatio="none" viewBox="0 0 4 100">
<path fill="none" stroke="#000000" stroke-width="1" class="triangle" d="M 0 0 V 100 0" />
</svg>
<svg preserveAspectRatio="none" viewBox="0 0 4 100">
<path fill="none" stroke="#000000" stroke-width="1" class="triangle" d="M 0 0 V 100 0"/>
</svg>
You can also simplify your stroke animation by using the stroke-dasharray attributes specifying 2 arguments:
dashlength
dash gap
stroke-dasharray="50 100"
You can even skip the length calculation getTotalLength() by applying these fixed initial attributes pathLength="100" and stroke-dasharray="0 100".
This way you can work with percentages without the need to calculate the exact length of your element.
let triangles = document.querySelectorAll(".triangle");
// Find scroll percentage on scroll
window.addEventListener("scroll", (e) => {
drawLines(triangles);
});
function drawLines(triangle, pathLength) {
var scrollpercent =
(document.body.scrollTop + document.documentElement.scrollTop) /
(document.documentElement.scrollHeight -
document.documentElement.clientHeight);
triangles.forEach((triangle) => {
var dashLength = 100 * scrollpercent;
triangle.setAttribute("stroke-dasharray", `${dashLength} 100`);
});
}
body {
padding:0 5em;
height: 200vh;
margin:0;
}
svg {
position: relative;
top: 15%;
left: 50%;
width: 50px;
height: 710px;
}
path{
transition:0.4s 0.4s;
}
<svg preserveAspectRatio="none" viewBox="0 0 4 100">
<path fill="none" stroke="#000000" stroke-width="1" class="triangle" d="M 0 0 V 100 0" pathLength="100" stroke-dasharray="0 100"/>
</svg>
<svg preserveAspectRatio="none" viewBox="0 0 4 100">
<path fill="none" stroke="#000000" stroke-width="1" class="triangle" d="M 0 0 V 100 0" pathLength="100" stroke-dasharray="0 100"/>
</svg>

ExtrudeGeometry issue for SVG Containing Hole

I am currently looking for extruding the SVG below:
But I get the following result:
I would extrude only the two hexagon wall space. How can I do it ?
I tried both SVGLoader.createShapes(path); and //path.toShapes(true);
Here is my code:
const loader = new SVGLoader();
// load a SVG resource
loader.load(
// resource URL
'./svg/hexagone.svg',
// called when the resource is loaded
function (data) {
const paths = data.paths;
const svgGroup = new THREE.Group();
svgGroup.name = "svgGroup"
svgGroup.scale.y *= -1;
let mesh;
for (let i = 0; i < paths.length; i++) {
const path = paths[i];
const material = new THREE.MeshNormalMaterial();
const shapes = SVGLoader.createShapes(path); //path.toShapes(true);
shapes.forEach((shape, i) => {
const geometry = new THREE.ExtrudeGeometry(shape, {
depth: 10,
bevelEnabled: false
});
geometry.computeVertexNormals();
mesh = new THREE.Mesh(geometry, material);
svgGroup.add(mesh);
});
}
// Reshape items
const box = new THREE.Box3().setFromObject(svgGroup);
const size = new THREE.Vector3();
box.getSize(size);
const zOffset = size.z / -2;
const yOffset = size.y / -2;
const xOffset = size.x / -2;
svgGroup.children.forEach(item => {
item.position.z = zOffset;
item.position.x = xOffset;
item.position.y = yOffset;
});
scene.add(new THREE.AxesHelper(5))
scene.add(svgGroup)
// Rotate
svgGroup.rotateX(90 * Math.PI / 180);
}
My add my SVG below. When I tried to debug, I get 4 meshes. 1 for each line, 1 for the outside hexagon (overlapping the inside hexagon), 1 for the inside hexagon.
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 53.86 53.86">
<defs>
<style>
.cls-1 {
fill: #fff;
}
</style>
</defs>
<g>
<polygon class="cls-1" points="15.98 53.36 .5 37.88 .5 15.98 15.98 .5 37.88 .5 53.36 15.98 53.36 37.88 37.88 53.36 15.98 53.36"/>
<path d="M37.67,1l15.19,15.19v21.48l-15.19,15.19H16.19L1,37.67V16.19L16.19,1h21.48m.41-1H15.77L0,15.77v22.31l15.77,15.77h22.31l15.77-15.77V15.77L38.08,0h0Z"/>
</g>
<g>
<polygon class="cls-1" points="20.63 44.31 10.96 34.64 10.96 20.96 20.63 11.29 34.31 11.29 43.98 20.96 43.98 34.64 34.31 44.31 20.63 44.31"/>
<path d="M34.1,11.79l9.38,9.38v13.26l-9.38,9.38h-13.26l-9.38-9.38v-13.26l9.38-9.38h13.26m.41-1h-14.09l-9.96,9.96v14.09l9.96,9.96h14.09l9.96-9.96v-14.09l-9.96-9.96h0Z"/>
</g>
</svg>
With another SVG (see below) the result is the same even if in this scenario I have 2 paths in the SVG.
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Fusion 360, Shaper Origin Export Add-In, Version 1.6.10 -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:shaper="http://www.shapertools.com/namespaces/shaper" shaper:fusionaddin="version:1.6.10" width="3.1176915cm" height="3.6cm" version="1.1" x="0cm" y="0cm" viewBox="0 0 3.1176915 3.6" enable-background="new 0 0 3.1176915 3.6" xml:space="preserve">
<path d="M-0,-1.8 L1.5588457,-0.9 1.5588457,0.9 -0,1.8 -1.5588457,0.9 -1.5588457,-0.9 -0,-1.8z" transform="matrix(1,0,0,-1,1.5588457,1.8)" fill="rgb(0,0,0)" stroke-linecap="round" stroke-linejoin="round" />
<path d="M-0.9588457,0.5535898 L-0.9588457,-0.5535898 -0,-1.1071797 0.9588457,-0.5535898 0.9588457,0.5535898 0,1.1071797 -0.9588457,0.5535898z" transform="matrix(1,0,0,-1,1.5588457,1.8)" fill="rgb(255,255,255)" stroke="rgb(0,0,0)" stroke-width="0.0010cm" stroke-linecap="round" stroke-linejoin="round" />
</svg>
Hexagon inside is also recovered by the biggest.
I finally chose to use lnkscape to generate my SVG. I have mush more options and I was able to generate the hole by combining all pathes in 1 path.

Node JS Sharp - Maintain SVG Font When Converting To JPG

I am using the sharp library to create dynamic JPEG license plate images.
Basically, I have a PNG that is a vanity license plate with no numbers. Then I create an svg in code like so
const svg = new Buffer(
`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="${x} ${y} 500 40">
<defs>
<style type="text/css">
<![CDATA[
#font-face {
font-family: LicensePlate;
src: url('LicensePlate.ttf');
}
svg {
width: 100%;
height: 100%;
}
]]>
</style>
</defs>
<text x="0" y="0" font-family="LicensePlate" font-size="${fontsize}" letter-spacing="${letterspace}">
${platenumber.toUpperCase()}
</text>
</svg>`
);
Passing in the desired width, height, and license plate number. Then I use the sharp library to overlay my SVG in the middle of the license plate. This all works just fine.
However, I have imported a custom license plate font (LicensePlate.ttf). In order to debug my in-code SVG image I made an actual svg image file that I open in the browser to make sure that it all looks correct, which it does.
The problem is that when the final JPEG file is created it does not contain my custom font. Instead it falls back on Verdana.
My question is, is there any way I can maintain the SVG font while creating the image with sharp?
Thanks!
Full Code
function createImage(platenumber) {
//Trying to create some sort of responsiveness
let fontsize = 80;
let letterspace = 10;
let width = 300;
let height = 90;
let x = 0;
let y = -45;
const inputlength = platenumber.length;
//Minumum Length
if (inputlength == 2) {
x = -200;
}
if (inputlength == 3) {
x = -150;
}
if (inputlength == 4) {
x = -130;
}
if (inputlength == 5) {
x = -105;
}
if (inputlength == 6) {
x = -65;
}
try {
console.log('stream is duplex, ', pipe instanceof stream.Duplex);
//Read the svg code into a buffer with a passed in plate number
const svg = new Buffer(
`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="${x} ${y} 500 40">
<defs>
<style type="text/css">
<![CDATA[
#font-face {
font-family: LicensePlate;
src: url('LicensePlate.ttf');
}
svg {
width: 100%;
height: 100%;
}
]]>
</style>
</defs>
<text x="0" y="0" font-family="LicensePlate" font-size="${fontsize}" letter-spacing="${letterspace}">
${platenumber.toUpperCase()}
</text>
</svg>`
);
const plateid = rand.generate(10);
//Create a write stream to a randomly generated file name
const write = new fs.createWriteStream(`plates/${plateid}.jpg`);
//Create the sharp pipeline
const pipeline = pipe
.overlayWith(svg, { gravity: sharp.gravity.center })//we center the svg image over the top of whatever image gets passed into the pipeline
.jpeg();//we convert to JPG because it is a compressed file format and will save space (we could also do webp if we really want to be slick about it)
//Create the read stream from the license plate template
const read = new fs.createReadStream('plate-2.png')
.pipe(pipeline)//pipe out sharp pipeline
.pipe(write);//add the write stream so that our sharp pipeline knows where to put the image
return plateid;
} catch (e) {
console.log(e);
return null;
}
}
SVG Image
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="90" viewBox="0 -50 500 40">
<defs>
<style type="text/css">
<![CDATA[
#font-face {
font-family: LicensePlate;
src: url('LicensePlate.ttf');
}
]]>
</style>
</defs>
<text x="0" y="0" font-family="LicensePlate" font-size="150" letter-spacing="10">
Pl#T3#
</text>
</svg>
Exact Font
Here is a link to the exact font I used
http://www.fontspace.com/dave-hansen/license-plate
I dealt with this problem by installing the font in my OS, when the file is being converted, the libraries can only access OS fonts.

How can i make a svg <rect> box dynamic with changeable <text>?

How can i make a svg rect box dynamic with changeable text ? Like in my code, if the text "Hello" will more than 30 character ?
<svg version="1.1" viewBox="0 0 500 500" preserveAspectRatio="xMinYMin meet" class="svg-content">
<g>
<rect x="0" y="0" width="100" height="100" stroke-width="5" stroke="#000000" fill="none"></rect> ?
<text x="0" y="50" font-family="Verdana" font-size="35" fill="blue">Hello</text>
</g>
</svg>
Probably the best approach would be to have the rect preset at specific width. Then create tspans to fill the text element, and dynamically resize the rect height as the characters exceed the preset width.
Below is an example:
<!DOCTYPE HTML>
<html>
<head>
<title>Wrap Text Rectangle</title>
</head>
<body onload=wrapTextRect()>
Place this text:<br>
<textarea id="myTextValue" style='width:400px;height:60px;'>
Hello!
</textarea><br>
<button onClick=wrapTextRect()> Wrap text in rect</button>
<div id="svgDiv" style='background-color:lightgreen;width:400px;height:400px;'>
<svg id="mySVG" width="400" height="400">
<rect id=myRect x=50 y=50 rx=10 ry=10 width=100 fill="#4682b4" stroke='black' stroke-width=5 opacity=.5 />
<text id=myText font-size=14 font-family="arial" fill="white" />
</svg>
</div>
SVG Source:<br>
<textarea id=sourceValue style=width:500px;height:300px></textarea>
<script>
var NS="http://www.w3.org/2000/svg"
//---onload and button---
function wrapTextRect()
{
//---clear previous---
for(var k=myText.childNodes.length-1;k>=0;k--)
myText.removeChild(myText.childNodes.item(k))
var padding=10
var width=+myRect.getAttribute("width")-padding
var x=+myRect.getAttribute("x")
var y=+myRect.getAttribute("y")
var fontSize=+myText.getAttribute("font-size")
var text=myTextValue.value
var words = text.split(' ');
var text_element = document.getElementById('myText');
var tspan_element = document.createElementNS(NS, "tspan"); // Create first tspan element
var text_node = document.createTextNode(words[0]); // Create text in tspan element
tspan_element.setAttribute("x", x+padding);
tspan_element.setAttribute("y", y+padding+fontSize);
tspan_element.appendChild(text_node); // Add tspan element to DOM
text_element.appendChild(tspan_element); // Add text to tspan element
//---[EDIT] a single word that exceeds preset rect with---
if(words.length==1)
{
var len = tspan_element.getComputedTextLength()
if(len>+myRect.getAttribute("width"))
myRect.setAttribute("width", len+2*padding)
}
//---end [EDIT]------------------
for(var i=1; i<words.length; i++)
{
var len = tspan_element.firstChild.data.length // Find number of letters in string
tspan_element.firstChild.data += " " + words[i]; // Add next word
if (tspan_element.getComputedTextLength() > width-padding)
{
tspan_element.firstChild.data = tspan_element.firstChild.data.slice(0, len); // Remove added word
var tspan_element = document.createElementNS(NS, "tspan"); // Create new tspan element
tspan_element.setAttribute("x", x+padding);
tspan_element.setAttribute("dy", fontSize);
text_node = document.createTextNode(words[i]);
tspan_element.appendChild(text_node);
text_element.appendChild(tspan_element);
}
}
var height = text_element.getBBox().height +2*padding; //-- get height plus padding
myRect.setAttribute('height', height); //-- change rect height
//---show svg source---
sourceValue.value=svgDiv.innerHTML
}
</script>
</body>
</html>

How to properly set width and height of SVG in javascript?

I am setting the width and height of a SVG image in javascript and it does not work. My code is like this:
var url = "https://dl.dropboxusercontent.com/u/74728667/left_arrow.svg";
var main = document.getElementById("main");
var xhr = new XMLHttpRequest();
xhr.open('get', url, true);
xhr.onreadystatechange = function(ev)
{
if (xhr.readyState === 4)
{
if (xhr.status === 200)
{
var e = xhr.responseXML.documentElement;
var svg = document.importNode(e, true);
svg.setAttribute("width", "28px");
svg.setAttribute("height", "28px");
svg.style.border="1px solid black";
svg.style.position="absolute";
svg.style.left="50px";
svg.style.top="50px";
main.appendChild(svg);
}
else
{
alert("request failed");
}
}
};
xhr.send();
Above code produces a result like this:
Note the SVG is not resized to the specified width and height. how can i fix this?. i have tried using svg.setAttributeNS(null, "width", "28px");
I inspected the resulting html produced by above code and if I copy paste it in a separate fiddle, I get this:
HTML:
<div id="main" style="position:relative"><svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 18 18" width="28px" height="28px" style="border: 1px solid black; position: absolute; left: 50px; top: 50px;">
<path fill="#a00" fill-opacity="1" stroke="none" d="M 0,9 l 17,9 -6,-9 6,-9 Z" onmouseover="evt.target.setAttribute('fill', '#ac0');" onmouseout="evt.target.setAttribute('fill','#a00');"></path>
</svg></div>
Output:
How can I make my js code produce the same result as above?
This probrem is caused by spell miss of view"b"ox in source svg file.
Web browser doesn't treat view"b"ox attribute as view"B"ox attribute, because stand alone svg file is xml file.
Thus you should correct the svg source like this.
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 18 18"
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"
But these attributes are treated as same at inline svg in html.
This act is defined by HTML parsing rule, see this.
http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#creating-and-inserting-nodes

Resources