Skip to content

Instantly share code, notes, and snippets.

@WolfgangFahl
Last active January 13, 2019 10:07
Show Gist options
  • Save WolfgangFahl/22361573b05b541ac9799037116aea8d to your computer and use it in GitHub Desktop.
Save WolfgangFahl/22361573b05b541ac9799037116aea8d to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<!--
see also https://stackoverflow.com/questions/54151068/d3-multiple-different-graticules
-->
<html>
<head>
<meta http-equiv="content-type"
content="application/xhtml+xml; charset=utf-8"/>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v1.min.js"></script>
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<title>IFMMS Logo</title>
<style type="text/css">
.leftlabel {
color: gray;
display: inline-block;
text-align: right;
width: 100px;
}
}
</style>
</head>
<body style=font-family:arial>
<fieldset>
<legend>Logo settings</legend>
<label class='leftlabel' for='pickerb'>background:</label>
<input id='pickerb' type='color' value='#FFFFFF'
onchange='changeColor(this,"background")'>
<select onchange='changeColor(this,"background","pickerb")'>
<option value="#FFFFFF">white</option>
<option value="#E8FFFF">aqua</option>
<option value="#F8F8F8">light gray</option>
</select>
<label class='leftlabel' for='pickers'>stroke:</label>
<input id='pickers' type='color' value='#000000'
onchange='changeColor(this,"graticule")'><select onchange='changeColor(this,"graticule","pickers")'>
<option value="#000000">black</option>
<option value="#0000FF">blue</option>
<option value="#808080">gray</option>
</select>
<br>
<label class='leftlabel' for='rowsSlider'>rows:</label>
<input id="rowsSlider" type="range" min="1" max="5" value="1"
onChange="changeSettings(this)"/><label id="rowsLabel"></label>
<label class='leftlabel' for='colsSlider'>columns:</label>
<input id="colsSlider" type="range" min="1" max="5" value="1"
onChange="changeSettings(this)"/><label id="colsLabel"></label>
<br>
<label class='leftlabel' for='latstepsSlider'>lat steps:</label>
<input id="latstepsSlider" type="range" min="4" max="20" value="10"
onChange="changeSettings(this)"/><label id="latstepsLabel"></label>
<label class='leftlabel' for='lonstepsSlider'>lon steps:</label>
<input id="lonstepsSlider" type="range" min="4" max="20" step="2" value="8"
onChange="changeSettings(this)"/><label id="lonstepsLabel"></label>
<br>
<label class='leftlabel' for='scaleSlider'>scale:</label>
<input id="scaleSlider" type="range" min="25" max="2000" step="25" value="200"
onChange="changeSettings(this)"/><label id="scaleLabel"></label>
<label class='leftlabel' for='strokeScaleSlider'>stroke:</label>
<input id="strokeScaleSlider" type="range" min="5" max="50" step="1" value="15"
onChange="changeSettings(this)"/><label id="strokeScaleLabel"></label>
<label class='leftlabel' for='symbolSlider'>symbol:</label>
<input id="symbolSlider" type="range" min="50" max="300" step="1" value="100"
onChange="changeSettings(this)"/><label id="symbolLabel"></label>
<br>
<label class='leftlabel' for='dxSlider'>dx:</label>
<input id="dxSlider" type="range" min="-500" max="500" step="1" value="0"
onChange="changeSettings(this)"/><label id="dxLabel"></label>
<label class='leftlabel' for='dySlider'>dy:</label>
<input id="dySlider" type="range" min="-500" max="500" step="1" value="0"
onChange="changeSettings(this)"/><label id="dyLabel"></label>
<br>
<label class='leftlabel' for='debugCheckbox'>debug:</label>
<input id="debugCheckbox" type=checkbox onclick="changeSettings(this)"/>
<label class='leftlabel' for='showSourceCheckbox'>showSource:</label>
<input id="showSourceCheckbox" type=checkbox onclick="changeSettings(this)"/>
<label class='leftlabel' for='utf8Checkbox'>UTF-8:</label>
<input id="utf8Checkbox" type=checkbox onclick="changeSettings(this)"/>
<label class='leftlabel' for='mollWeideCheckbox'>mollWeide:</label>
<input id="mollWeideCheckbox" type=checkbox onclick="changeSettings(this)"/>
</fieldset>
<h3>Logo drafts</h3>
<div id='container'></div>
<h3 id="sourcesTitle"></h3>
<div id='source'></div>
<script>
// global settings
var settings = {
rows: 1,
cols: 1,
dx: 0,
dy: 0,
lonsteps: 6,
latsteps: 10,
strokeScale: 15,
background: "white",
graticuleStroke: "black",
debug: false
};
/**
* get the slider value and change the corresponding label
* return the value
* @param {prefix} - the prefix of the Slider and label
*/
function getAndChangeSliderLabel(prefix) {
var value=getSliderValue(prefix)
setLabelValue(prefix,value)
return value;
}
/**
* get the label value
* @param {prefix} - the prefix of the Label
*/
function setLabelValue(prefix,value) {
var label=document.getElementById(prefix+"Label");
label.innerHTML=value;
}
/**
* get the slider value
* return the value
* @param {prefix} - the prefix of the Slider
*/
function getSliderValue(prefix) {
var slider=document.getElementById(prefix+"Slider");
var value=slider.value;
return parseInt(value);
}
/**
* get the checkBox value
* return the value
* @param {prefix} - the prefix of the checkBox
*/
function getCheckBoxValue(prefix) {
var box=document.getElementById(prefix+"Checkbox");
return box.checked
}
function changeSettings() {
dochangeSettings(settings)
}
/**
* modify the rows and columns
*/
function dochangeSettings(settings) {
settings.rows=getAndChangeSliderLabel("rows");
settings.cols=getAndChangeSliderLabel("cols");
var startScale=getAndChangeSliderLabel("scale");
var symbolScale=getAndChangeSliderLabel("symbol");
settings.strokeScale=getAndChangeSliderLabel("strokeScale");
settings.dx=getAndChangeSliderLabel("dx");
settings.dy=getAndChangeSliderLabel("dy");
settings.lonsteps=getAndChangeSliderLabel("lonsteps");
settings.latsteps=getAndChangeSliderLabel("latsteps");
var debug=getCheckBoxValue("debug")
var showSource=getCheckBoxValue("showSource")
var mollWeide=getCheckBoxValue("mollWeide");
var utf8=getCheckBoxValue("utf8");
sample(startScale,symbolScale,utf8,mollWeide,debug,showSource);
}
/**
* change the color getting the value from the given picker
* using the given attributeName
* @param {picker} - the picker for the color
* @param {attributeName} - the attribute of the setting to be changed
* @param {otherPickerId} - the id of the otherPicker to be changed (if any)
*/
function changeColor(picker, attributeName,otherPickerId) {
var color=picker.value;
if (attributeName==="background")
settings.background=color;
if (attributeName==="graticule")
settings.graticuleStroke=color;
if (otherPickerId) {
otherPicker=document.getElementById(otherPickerId);
otherPicker.value=color;
}
changeSettings()
}
/**
* use the given svg element
* @param {svg} - the svg element in which to use the element
* @param {id} - the id of the element to be used
*/
function use(svg,id) {
return uset(svg,id,1,1,0,0)
}
/**
* use the given element translated and scaled
* @param {svg} - the svg element in which to use the element
* @param {id} - the id of the element to be used
* @param {sx} - scale factor x
* @param {sy} - scale factor y
* @param {tx} - translation x
* @param {ty} - translation y
*/
function uset(svg,id,sx,sy,tx,ty) {
return svg.append("g").append("use")
.attr("xlink:href","#"+id)
.attr("transform","translate("+tx+","+ty+") scale("+sx+","+sy+")")
}
/**
* append a rectangle to the given parent
* @param {parent} - the parent to append to
* @param {id} - the id for the rectangle
* @param {x} - the x position in pixels for the rectangle
* @param {y} - the y position in pixels for the rectangle
* @param {width} - the width in pixels for the rectangle
* @param {height} - the height in pixels for the rectangle
* @param {stroke} - the stroke color of the rectangle
*/
function rect(parent,id,x,y,width,height,stroke) {
parent.append("rect")
.attr("id",id)
.attr("x",x)
.attr("y",y)
.attr("width",width)
.attr("height",height)
.attr("fill","#ccc")
.attr("opacity",.3)
.attr("stroke",stroke)
.attr("stroke-width",3);
}
/**
* create an IFMMS logo draft
*
* @param {title} - the title to use
* @param {svgid} - the id of the svg element to insert the logo into
* @param {cx} - base x
* @param {cy} - base y
* @param {scale} - the scale factor of the projection
* @param {symbolScale} - the scale of the symbol in percent
* @param {lonsteps} - how many steps around the globe from east to west
* @param {latsteps} - how many steps around the globe from south to north
* @param {utf8} - use UTF-8 symbol
* @param {mollWeide} - use MollWeide projection
* @param {debug} - show debug information
* @returns - the bounding box for the logo
*/
function createLogo(title,svgid,cx,cy,scale,symbolScale,lonsteps,latsteps,utf8,mollWeide,debug) {
var svg= d3.select("#"+svgid); // context
// get a projection
var projection=d3.geoAitoff().scale(scale);
if (mollWeide)
projection=d3.geoMollweide();
//----build svg graticule----
var path = d3.geoPath().projection(projection);
var eps=0.01;
var lonstep=(360-eps)/lonsteps;
var latstep=(360-eps)/latsteps;
// create a graticule
var graticule = d3.geoGraticule().step([lonstep, latstep]);
// set the title of the SVG
svg.append("title").text(title);
// create a defs node
var defs=svg.append("defs");
var ggraticule=defs.append("g")
.attr("id","graticule"+svgid)
.attr("fill", "none")
.attr("stroke", settings.graticuleStroke)
.attr("stroke-width",scale/settings.strokeScale);
var schlegelUndEisen=defs.append("g")
.attr("id","Schlaegel_und_Eisen_nach_DIN_21800"+svgid)
.append("path")
.attr("fill","black")
.attr("d","M83.526 24.3292l5.0006 5.0005 9.8445 19.7439 -19.7439 -9.8445 -4.825 -4.8248 -19.3012 19.9984 31.5273 30.4582c0.7699,0.7437 0.818,1.9607 0.1092,2.7628 -1.3648,1.5446 -2.826,3.0058 -4.3705,4.3705 -0.8021,0.7088 -2.0191,0.6608 -2.7628,-0.1091l-30.3544 -31.4199 -30.3246 31.4186c-0.7436,0.7704 -1.961,0.8188 -2.7634,0.1098 -1.5441,-1.3645 -3.005,-2.8251 -4.3693,-4.3692 -0.709,-0.8023 -0.6607,-2.0199 0.1098,-2.7635l31.5228 -30.425 -20.8788 -21.6126c-3.7644,4.233 -6.9481,8.9965 -9.4369,14.166l-10.8805 -9.4668c6.8998,-12.6804 17.338,-23.1096 30.0058,-30.0183l9.4619 10.879c-5.1582,2.4839 -9.9086,5.6625 -14.1303,9.4194l21.7166 20.9795 20.0988 -19.3983 -11.3677 -11.3679 9.8994 -9.8994 11.5433 11.5435 1.4376 -1.3877 4.6204 4.618 -1.3887 1.4389z")
.attr("transform","translate(0,0)");
ggraticule.selectAll("path.feature"+svgid)
.data(graticule.lines)
.enter().append("path")
.attr("class", "feature"+svgid)
.attr("d", path);
defs.append("path")
.attr("id","background"+svgid)
.datum(graticule.outline)
.attr("fill", settings.background)
.attr("d", path);
// https://stackoverflow.com/questions/19134995/how-to-display-unicode-in-svg
// https://www.utf8icons.com/character/9874/hammer-and-pick
// unicode hex 2692
// html &#9874;
// http://bl.ocks.org/eweitnauer/7325338
defs.append("text")
.attr("id","hammerandpick"+svgid)
.attr("font-size",scale*3.3)
.attr("text-anchor","middle")
.attr("dominant-baseline","central")
.attr("x",0)
.attr("y",0)
.text("⚒");
use(svg,"background"+svgid);
var guse=use(svg,"graticule"+svgid);
var bbox = guse.node().getBBox();
if (utf8) {
uset(svg,"hammerandpick"+svgid,symbolScale/100,symbolScale/100,cx+settings.dx,cy-0.54*scale+settings.dy);
} else {
uset(svg,"Schlaegel_und_Eisen_nach_DIN_21800"+svgid,scale*symbolScale/3500,scale*symbolScale/3500,cx-1.39*scale+settings.dx,cy-1.56*scale+settings.dy);
}
if (debug) {
rect(svg,"border"+svgid,bbox.x,bbox.y,bbox.width,bbox.height,"#666")
}
return bbox;
}
/**
* get the id for the given row and column
*/
function getId(row,col) {
return "logo"+row+col;
}
/**
* create an svg element with the given id, viewbox, width and height
* @param {vx} - view box x
* @param {vy} - view box y
* @param {vw} - view box width
* @param {vh} - view box height
* @param {width} - the width in pixels for the svg element
* @param {height} - the height in pixels for the svg element
*/
function createSvg(id,vx,vy,vw,vh,width,height) {
var svg=document.createElementNS('http://www.w3.org/2000/svg','svg');
svg.setAttribute('id',id);
svg.setAttribute('version','1.1');
svg.setAttribute('xmlns:svg','http://www.w3.org/2000/svg');
svg.setAttribute('xmlns:xlink','http://www.w3.org/1999/xlink');
svg.setAttribute('viewBox',vx+' '+vy+' '+vw+' '+vh);
//svg.setAttribute('position','absolute');
//svg.setAttribute('x',x);
//svg.setAttribute('y',y);
svg.setAttribute('width',width);
svg.setAttribute('height',height);
svg.setAttribute('overflow','visible');
return svg;
}
/**
* encode the given HMTL string
* https://stackoverflow.com/a/48073476/1497139
* @param {str} - the html string to encode
*/
function encodeHTML(str){
return str.replace(/[\u00A0-\u9999<>&](?!#)/gim, function(i) {
return '&#' + i.charCodeAt(0) + ';';
});
}
/**
* show the source for the given element
* @param {sourceId} - the id of the element to get the HTML source from
* @param {targetId} - the id of the element where to insert the code
* @param {title} - the title to use
*/
function showSource(sourceId,targetId,title) {
var h3=document.createElement('h3')
h3.innerHTML=title;
var pre=document.createElement('pre');
var source=document.getElementById(sourceId);
var target=document.getElementById(targetId);
pre.innerHTML=encodeHTML(source.outerHTML.replace(/[<]/g,"\n<"));
target.appendChild(h3);
target.appendChild(pre);
}
/**
* format the given value with the given prefix
* @param {prefix} - the prefix to use
* @param {value} - the title to use
* @returns {String} - the formatted String
*/
function format(prefix,value) {
return prefix+": "+Math.round(value)+" "
}
/**
* get a formatted bounded Box string
*/
function formatBBox(x,y,width,height) {
var bboxText=format("x",x)+format("y",y)+format("w",width)+format("h",height)
return bboxText;
}
/**
* show the given bounding box values as a pre in the given container
*/
function showBox(container,x,y,width,height) {
pre=document.createElement("pre")
pre.innerHTML=formatBBox(x,y,width,height)
container.appendChild(pre)
}
/**
* show a sample of drafts
* @param {startScale} - scale to startWith
* @param {symbolScale} - the scale of the symbol in percent
* @param {utf8} - use UTF-8 symbol
* @param {mollWeide} - use MollWeide projection
* @param {debug} - show debug information
* @param {doShowSource} - show the Sources
*/
function sample(startScale,symbolScale,utf8,mollWeide,debug,doShowSource) {
var pi=3.1415926
var container=document.getElementById('container');
var source=document.getElementById('source');
// clear the two areas
container.innerHTML=""
source.innerHTML=""
var debug2=false;
// loop over the rows and columns
for (var row = 0; row <settings.rows; row++) {
for (var col = 0; col < settings.cols; col++) {
// calculate a unique id for the logo
var id=getId(row,col);
// base calculation parameters
var cx=480;
var cy=252;
// scale
var dScale=row*25;
var scale=startScale+dScale;
var height=scale;
var width=scale*pi/2;
var rscale=scale-50;
// create an svg element with the calculated id
vx=cx-scale*(pi);
vy=cy-scale*(pi/2);
vw=314+rscale*2*pi;
vh=157+rscale*pi;
if (debug2) {
showBox(container,vx,vy,vw,vh)
}
var svg=createSvg(id,vx,vy,vw,vh,width,height,scale);
container.appendChild(svg);
var lonsteps=settings.lonsteps+col*2;
var latsteps=settings.latsteps;
var title='IFMMS Logo';
title=title+' scale '+scale+' steps '+lonsteps+'/'+latsteps;
// create a logo into the svg element with the given id
bbox=createLogo(title,id,cx,cy,scale,symbolScale,lonsteps,latsteps,utf8,mollWeide,debug);
if (debug2) {
showBox(container,bbox.x,bbox.y,bbox.width,bbox.height)
}
if (doShowSource) {
sourcesTitle.innerHTML="Sources"
showSource(id,'source',title)
} else {
sourcesTitle.innerHTML=""
}
}
container.appendChild(document.createElement('br'));
}
}
changeSettings(settings)
//sample(1,5,true)
</script>
</body>
</html>
console.log('Hello World!');
/* todo: add styles */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment