Change the style of lines dynamically in Cesium - kml

The main part of code using Cesium.js is like that:
var viewer = new Cesium.Viewer('cesiumContainer');
viewer.dataSources.add(Cesium.KmlDataSource.load('flight-paths.kml');
In the flight-paths.kml, there are millions of nodes like this:
<Placemark>
<TimeSpan>
<begin>2007-01-01T01:50:00Z</begin>
</TimeSpan>
<styleUrl>#mairport0_icon0</styleUrl>
<LineString>
<tessellate>1</tessellate>
<altitudeMode>absolute</altitudeMode>
<coordinates>
-98.1796384517953,47.8244940960245,683352.4617486351 -99.3279951928504,48.3541860848452,689546.54119779
</coordinates>
</LineString>
</Placemark>
The style of the nodes defined like this:
Style id="mairport0_icon0">
<LineStyle>
<color>ff7fff85</color>
<width>1.5</width>
</LineStyle>
</Style>
There are lots of style definitions in the kml file which means I can't change the style definition manually. How can I get the lines in kml file and change the style of it?

If want to change the style programmatically in JavaScript rather than change the KML then you can change the properties of the entity after loading KML using the Cesium API.
Suppose you wanted to change the color of the line to RED with 50% transparency then try the following:
var source = new Cesium.KmlDataSource();
source.load('flight-paths.kml').then(function(){
var entities = source.entities.values;
for(var i =0; i < entities.length; i++) {
var e = entities[i];
e.wall.outlineColor = Cesium.Color.RED.withAlpha(0.5);
}
});
viewer.dataSources.add(source);
Refer to other properties of the Entity wall field.
https://cesiumjs.org/Cesium/Build/Documentation/WallGraphics.html

Related

How to center childs in a layout on Apache Royale?

I search for a easy and efficient way to center all childs in a container using basic package. (Because mixing basic and jewel layouts leads to some unwanted sides effects)
Must I use CSS, beads ? If both are usable what is difference and can I have code sample of each ?
For exemple could you tell me how to modify this code to center the text label:
<js:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:j="library://ns.apache.org/royale/jewel"
xmlns:js="library://ns.apache.org/royale/basic" xmlns:local="*">
<js:valuesImpl>
<js:SimpleCSSValuesImpl />
</js:valuesImpl>
<js:initialView>
<js:View>
<js:beads>
<js:VerticalLayout/>
</js:beads>
<js:Label text="How to center me on horizontal axis ?"/>
</js:View>
</js:initialView>
</js:Application>
Thanks to yishayw, as I'm not familiar with how to add css, I find how to do it and put the full working code here. I would expect that js|View would trigger the style but looking on css with browser I saw that style name for first "view" is "royale"
<js:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:j="library://ns.apache.org/royale/jewel"
xmlns:js="library://ns.apache.org/royale/basic" xmlns:local="*">
<fx:Style>
#namespace j "library://ns.apache.org/royale/jewel";
#namespace js "library://ns.apache.org/royale/basic";
.royale {
align-items :center;
}
js|Label {
color: #ff0000;
}
</fx:Style>
<js:valuesImpl>
<js:SimpleCSSValuesImpl />
</js:valuesImpl>
<js:initialView>
<js:View >
<js:beads>
<js:VerticalFlexLayout />
</js:beads>
<js:Label text="How to center me on horizontal axis ?"/>
</js:View>
</js:initialView>
</js:Application>
Regards
Try using VerticalFlexLayout and adding align-items: center to the style of the container.

How can i loop through a list and create/add to a kml using simplekml?

I have a list of coordinates, and i want to create a kml that has all of these linestings based on the list 'coord'. I have it almost working, however, it will only create the linestring for the last coordinate in the list. How can i use append (or something like that) the kml file in simplekml, so i can loop through my list and create a new line string for each index in 'coord' and put them all in one kml?
This is what 'coord' looks like
[[(-89.60233333333333, 29.178833333333333), (-88.91166666666666, 30.255)], [(-88.73, 30.283833333333334), (-87.82583333333334, 30.298333333333332)], [(-87.6, 30.13), (-87.6, 30.05)], [(-87.9, 30.05), (-87.9, 30.13)], [(-87.95733333333334, 30.254), (-87.9825, 30.655833333333334)], [(-87.82366666666667, 30.282333333333334), (-88.7285, 30.278166666666667)], [(-88.88983333333333, 30.229333333333333), (-89.58366666666667, 29.154166666666665)], [(-89.88433333333333, 29.19333333333333), (-90.2005, 30.415666666666667)], [(-91.00733333333334, 30.875333333333334), (-95.13933333333334, 29.981166666666667)], [(-95.46, 29.272333333333332), (-95.46183333333333, 28.965)], [(-95.607, 28.9765), (-95.30183333333333, 29.2585)]]
for idx, val in enumerate(coord):
kml = simplekml.Kml()
ls = kml.newlinestring(name='A LineString {0}'.format(idx))
ls.coords = coord[idx]
ls.extrude = 1
ls.altitudemode = simplekml.AltitudeMode.relativetoground
ls.style.linestyle.width = 5
ls.style.linestyle.color = simplekml.Color.red
kml.save('lines.kml')
my kml file then looks like this, which is what i want, but i just also want all of the other index's to be in there too not just the last one
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2">
<Document id="4347">
<Style id="4350">
<LineStyle id="4351">
<color>ff0000ff</color>
<colorMode>normal</colorMode>
<width>5</width>
</LineStyle>
</Style>
<Placemark id="4349">
<name>A LineString 10</name>
<styleUrl>#4350</styleUrl>
<LineString id="4348">
<coordinates>-95.607,28.9765,0.0 -95.30183333333333,29.2585,0.0</coordinates>
<extrude>1</extrude>
<altitudeMode>relativeToGround</altitudeMode>
</LineString>
</Placemark>
</Document>
</kml>
I got it figured out
for idx, val in enumerate(coord):
kml = simplekml.Kml()
idx = 0
for x in coord:
try:
idx +=1
ls = kml.newlinestring(name='A LineString {0}'.format(idx))
ls.coords.addcoordinates(coord[idx])
ls.extrude = 1
ls.style.linestyle.width = 5
ls.style.linestyle.color = simplekml.Color.blue
except:
pass
kml.save('lines.kml')

Wrap text to a circle shape in svg or canvas

What would be a good solution for fitting text to a circle on a website, so it flows with the curves of the circle, instead of a rectangular bounding box?
Here's what I'm trying to achieve:
There are a number of black circles (of a fixed size) on a page, with a textarea next to each of them.
When text is entered into the textarea, it appears in the black circle, where it is centered on both axes.
If so much text is entered that line becomes longer than the radius of the circle, minus a specified value for margin, the line will break like you would expect from regular wrapping, with the block of text still being centered.
Lines nearer the top or bottom will, of course, be shorter than the ones near the middle.
The text will have a fixed size and when the circle is filled with text, the extra content should not be shown (like overflow hidden).
The black circles with the text are really speech bubbles, which are meant to be printed and glued onto a poster.
Do any of the fantastic SVG/Canvas libraries support this or will I have to figure our a method from scratch?
There is a proposed CSS feature call "exclusions" that would make it possible to flow text inside defined areas: http://www.w3.org/TR/css3-exclusions/
This means that SVG and Canvas paths would likely be defined as containers and text would flow/wrap inside the containers.
I did say "proposed" -- it's a ways off from being a reality in browsers.
However...
You can fairly easily wrap text inside a circle using html canvas
The width available to display text on any line changes as you move down the circle.
Here’s how to determine the maximum available width of any horizontal line on a circle
// var r is the radius of the circle
// var h is the distance from the top of the circle to the horizontal line you’ll put text on
var maxWidth=2*Math.sqrt(h*(2*r-h));
You fit text to the line by measuring the width of text—adding one word at a time, until you’ve used up all the available width of that line.
Here’s how to use canvas to measure any text using the current context.font:
var width=ctx.measureText(“This is some test text.”).width;
The rest is just adding text to each line up to the maximum line width and then starting a new line.
If you prefer SVG, you can do similar in SVG using the element.getComputedTextLength method for text metrics.
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/upq6L/
<!doctype html>
<html lang="en">
<head>
<style>
body{ background-color: ivory; padding:20px; }
canvas{ border:1px solid red;}
</style>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script>
$(function() {
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var text = "'Twas the night before Christmas, when all through the house, Not a creature was stirring, not even a mouse. And so begins the story of the day of Christmas";
var font="12pt verdana";
var textHeight=15;
var lineHeight=textHeight+5;
var lines=[];
var cx=150;
var cy=150;
var r=100;
initLines();
wrapText();
ctx.beginPath();
ctx.arc(cx,cy,r,0,Math.PI*2,false);
ctx.closePath();
ctx.strokeStyle="skyblue";
ctx.lineWidth=2;
ctx.stroke();
// pre-calculate width of each horizontal chord of the circle
// This is the max width allowed for text
function initLines(){
for(var y=r*.90; y>-r; y-=lineHeight){
var h=Math.abs(r-y);
if(y-lineHeight<0){ h+=20; }
var length=2*Math.sqrt(h*(2*r-h));
if(length && length>10){
lines.push({ y:y, maxLength:length });
}
}
}
// draw text on each line of the circle
function wrapText(){
var i=0;
var words=text.split(" ");
while(i<lines.length && words.length>0){
line=lines[i++];
var lineData=calcAllowableWords(line.maxLength,words);
ctx.fillText(lineData.text, cx-lineData.width/2, cy-line.y+textHeight);
words.splice(0,lineData.count);
};
}
// calculate how many words will fit on a line
function calcAllowableWords(maxWidth,words){
var wordCount=0;
var testLine="";
var spacer="";
var fittedWidth=0;
var fittedText="";
ctx.font=font;
for(var i=0;i<words.length; i++){
testLine+=spacer+words[i];
spacer=" ";
var width=ctx.measureText(testLine).width;
if(width>maxWidth){
return({
count:i,
width:fittedWidth,
text:fittedText
});
}
fittedWidth=width;
fittedText=testLine;
}
}
}); // end $(function(){});
</script>
</head>
<body>
<p>Text wrapped and clipped inside a circle</p>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

SVG Text bounding box is different from browser to browser when using #font-face?

I am trying to place an SVG Text-element according to the width and height of the text by getting the bounding box using the getBBox() method.
If the text is using a websafe font, this works reasonably well across different browsers, but if the text is styled using #font-face and a custom webfont, then the width of the text is returned incorrectly in Firefox (Mac) and Safari (iOS).
It works perfectly in both Safari (Mac) and Chrome (Mac).
If the gray box has the same width as the text, then it works in that browser.
Does anybody have an idea on how to get the correct width of the text bounding box in all browsers?
The browser is calculating the bounding box before it has finished loading/applying #font-face, assuming you don't need IE, you can wrap your BBox calculation function inside a document.fonts.ready promise...
document.fonts.ready.then(() => const bbox = textEl.getBBox());
Here is an example at work that exhibits the problem and the fix:
const xmlns = "http://www.w3.org/2000/svg";
const correct = document.getElementById("correct");
const incorrect = document.getElementById("incorrect");
visualizeBBox(incorrect);
document.fonts.ready.then(()=> visualizeBBox(correct));
function visualizeBBox(el){
const bbox = el.getBBox();
const rect = document.createElementNS(xmlns, "rect");
for (prop in bbox) rect.setAttribute(prop, bbox[prop]);
document.querySelector("svg").appendChild(rect);
}
svg text {
font-family: 'Diplomata SC', serif;
}
svg rect {
stroke: red;
fill: none;
}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Diplomata+SC&display=swap" rel="stylesheet">
<svg xmlns="https://www.w3.org/2000/svg" width="600" height="400">
<text x="0" y="40" font-size="24" id="correct">Correct dimensions</text>
<text y="100" font-size="24" id="incorrect">Incorrect dimensions</text>
<svg>
Today I ran into similar issue. Duopixel is right that getBBox() might return momental metric which may be unexpected because external font hasn't been loaded yet and some standard font is used instead.
The problem in WebKit (tested in Chrome 24.0.1312.52 and 26.0.1389.0 canary) is that the browser defers external font loading until it is first effectively used anywhere on the page. So even if you wait for onreadystatechange to become "complete" you are not guaranteed to have font metrics ready when calling getBBox() - you may still be the first one rendering a text styled with external font, inserting it into the document and immediately calling getBBox() on it (my case).
My workaround instead of calling mySVGInitCode() directly I do:
$("body").append(
$("<div/>")
.attr("class", "force-external-font-loading")
.attr("style", "font-family: \"xkcd\";visibility:hidden;position:absolute")
.text("x")
);
setTimeout(function(){ mySVGInitCode() }, 100); // 100ms is just arbitrary waiting time which should be sufficient for fetching the external font on a fast network, anyone has a better solution?
As you can see I dynamically insert absolutely positioned styled piece of text to force external font loading (visibility:hidden is important here instead of display:none). Then I wait some time before I execute my SVG code which could potentially render something and then immediately ask for metrics.

Convert SVG polygon to path

I have a fairly large SVG file of administrative subdivisions that I need to work with in Raphael.JS (it has 600 polygons and weights 1.2 Mb).
Now, I need to convert these polygons to paths so that it works in Raphael. The great poly2path tool does that, but it doesn't support any batch command, so that each polygon's position relative to the others is lost.
Do you know of any tool to convert SVG polygons to paths? (I also have the AI file which was used to export the SVG).
Many thanks
Open your SVG in a web browser.
Run this code:
var polys = document.querySelectorAll('polygon,polyline');
[].forEach.call(polys,convertPolyToPath);
function convertPolyToPath(poly){
var svgNS = poly.ownerSVGElement.namespaceURI;
var path = document.createElementNS(svgNS,'path');
var pathdata = 'M '+poly.getAttribute('points');
if (poly.tagName=='polygon') pathdata+='z';
path.setAttribute('d',pathdata);
poly.getAttributeNames().forEach(function(name){
if(name !== 'points')
path.setAttribute(name, poly.getAttribute(name))
})
poly.parentNode.replaceChild(path,poly);
}
Using the Developer Tools (or Firebug) of the browser, use "Copy as HTML" (or Copy SVG) on the element to get the modified source onto the clipboard.
Paste into a new file and enjoy.
I have a demo of the above method (slightly modified) on my website:
http://phrogz.net/svg/convert_polys_to_paths.svg
There are two methods in use on that page; one (like the above) uses string-based techniques to get and set the points; the other uses the SVG DOM for accessing points and setting path commands.
As noted by #Interactive in the comments, you can do this via text-only transformations by:
Convert all <polyline and <polygon to <path
Change all points=" to d="M
For any elements that were <polygon>, you need to add z as the last character of the d attribute to connect the last point to the first. For example:
<polygon points="1,2 3,-4 5,6"/>
becomes
<path d="M1,2 3,-4 5,6z"/>
This 'hack' works because the specifications declare that a moveto command (M or m) followed by multiple coordinates is legal, with all coordinates after the first interpreted as lineto commands.
A clicky-bunty answer:
open the svg in inkscape vector graphics editor
select all objects (ctrl-a)
at the drop down menu point "path" select first entry "object to path" (shift-ctrl-c)
save the svg and check out the path properties
Might be not an appropriate answer (because with large files the program needs some time).
Little fix for polygon id, fill and stroke attributes save
var polys = document.querySelectorAll('polygon,polyline');
[].forEach.call(polys,convertPolyToPath);
function convertPolyToPath(poly){
var svgNS = poly.ownerSVGElement.namespaceURI;
var path = document.createElementNS(svgNS,'path');
var points = poly.getAttribute('points').split(/\s+|,/);
var x0=points.shift(), y0=points.shift();
var pathdata = 'M'+x0+','+y0+'L'+points.join(' ');
if (poly.tagName=='polygon') pathdata+='z';
path.setAttribute('id',poly.getAttribute('id'));
path.setAttribute('fill',poly.getAttribute('fill'));
path.setAttribute('stroke',poly.getAttribute('stroke'));
path.setAttribute('d',pathdata);
poly.parentNode.replaceChild(path,poly);
}
Copying everything from the developer tools seems pretty inconvenient. You could use an XSLT to transform polygons and polylines to paths:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" exclude-result-prefixes="svg"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<!-- Identity transform: Copy everything
(except for polygon/polyline, handled below) -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Turn polygons/polylines into paths,
copy all attributes and content
(except for #points: Will be matched
by template below) -->
<xsl:template match="svg:polygon|svg:polyline">
<path>
<xsl:apply-templates select="#*|node()"/>
</path>
</xsl:template>
<!-- Turn the points attribute into a d attribute -->
<xsl:template match="#points">
<xsl:attribute name="d">
<xsl:value-of select="concat('M',.)"/>
<!-- If we have a polygon, we need to make
this a closed path by appending "z" -->
<xsl:if test="parent::svg:polygon">
<xsl:value-of select="'z'"/>
</xsl:if>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Any attributes of the polygon/polyline elements will be carried over to the path element. This is also suitable for batch processing. You can run this using any XSLT processor (Saxon, Xalan, xsltproc, Altova...) or even in-browser, using the XSLTProcessor object, like:
var xsltProcessor = new XSLTProcessor();
xsltProcessor.importStylesheet(stylesheet);
var transformedSVG = xsltProcessor.transformToFragment(svgDocument).firstChild
(Similar question: Examples of polygons drawn by path vs polygon in SVG)
Building on #halfer's solution. If you have internal CSS and want to retain the class names, you can slightly modify this to:
var polys = document.querySelectorAll('polygon,polyline');
[].forEach.call(polys,convertPolyToPath);
function convertPolyToPath(poly){
var svgNS = poly.ownerSVGElement.namespaceURI;
var path = document.createElementNS(svgNS,'path');
var pathClass = poly.getAttribute('class');
var points = poly.getAttribute('points').split(/\s+|,/);
var x0=points.shift(), y0=points.shift();
var pathdata = 'M'+x0+','+y0+'L'+points.join(' ');
if (poly.tagName=='polygon') pathdata+='z';
path.setAttribute('id',poly.getAttribute('id'));
path.setAttribute('class', pathClass);
path.setAttribute('d',pathdata);
poly.parentNode.replaceChild(path,poly);
}

Resources