Keep / Exclude brushed data in parallel coordinates d3.js - javascript

I am working to this example and trying to give it the same functionality for keeping/excluding the information in the brush as this example by Kai.
I have reviewed the code and apparently, Kai's example is built upon V.2 of D3 (at least the script that it calls is there). Because of this most of the coordinate system is not based upon the d3.parcoords.js library. My question is, how would I be able get the same result using parcoords.js ? Do you think it will be needed to modify parcoords.js to include Kai's functionality? I think that the problem is that the method Kai uses conflicts with the parcoords.js methods to draw axes.
I reviewed the parcoords.js documentation and there are no indications of a method or function to extract this information, at least in the same way. I am fairly new to javascript, but any help would be appreciated.
EDIT:
The part of the code I am trying to port is at the very bottom.
<!doctype html>
<title>Decision making tool</title>
<link rel="stylesheet" type="text/css" href="_/d3%20library/d3.parcoords.css">
<link rel="stylesheet" type="text/css" href="_/d3%20library/style.css">
<style>
/* data table styles */
#grid {
height: 198px;
}
.row,
.header {
clear: left;
font-size: 12px;
line-height: 18px;
height: 18px;
}
.row:nth-child(odd) {
background: rgba(0, 0, 0, 0.05);
}
.header {
font-weight: bold;
}
.cell {
float: left;
overflow: hidden;
white-space: nowrap;
width: 100px;
height: 18px;
}
.col-0 {
width: 180px;
}
</style>
<script src="_/d3 library/d3.min.js"></script>
<script src="_/d3 library/d3.parcoords.js"></script>
<script src="_/d3 library/divgrid.js"></script>
<script src="_/script.js"></script>
//The header div includes the buttons that are shown below.
<div id="header">
<h1>EUI assesment tool</h1>
<button title="Zoom in on selected data" id="keep-data" disabled="disabled">Keep</button>
<button title="Remove selected data" id="exclude-data" disabled="disabled">Exclude</button>
</div>
<div id="chart" class="parcoords" style="height:600px;"></div>
<div id="grid"></div>
<script id="brushing">
// quantitative color scale
var green_to_red = d3.scale.linear()
.domain([40, 200])
.range(["green", "red"])
.interpolate(d3.interpolateLab);
var color = function(d) {
return green_to_red(d['Site EUI (kBtu/sq ft)']);
};
var parcoords = d3.parcoords()("#chart")
.color(color)
.margin({
top: 50,
left: 250,
bottom: 20,
right: 20
})
// .composite('lighter')
.alpha(0.25);
// load csv file and create the chart
d3.csv('_/data/Mock Data Chicago.csv', function(data) {
parcoords
.data(data)
.hideAxis(["Property Name", "Address", "Community Area", "ZIP Code", "# of Buildings", "ENERGY STAR Score"])
.mode("queue")
.rate(30)
.interactive()
.render()
.brushMode("1D-axes"); // enable brushing
// create data table, row hover highlighting
var grid = d3.divgrid();
d3.select("#grid")
.datum(data.slice(0, 5))
.call(grid)
.selectAll(".row")
.on({
"mouseover": function(d) {
parcoords.highlight([d])
},
"mouseout": parcoords.unhighlight
});
// update data table on brush event
parcoords.on("brush", function(d) {
d3.select("#grid")
.datum(d.slice(0, 5))
.call(grid)
.selectAll(".row")
.on({
"mouseover": function(d) {
parcoords.highlight([d])
},
"mouseout": parcoords.unhighlight
});
});
});
//Below is the code I copied from Kai's version. I think the if statement is not returning information for the buttons above to enable
if (selected.length < data.length && selected.length > 0) {
d3.select("#keep-data").attr("disabled", null);
d3.select("#exclude-data").attr("disabled", null);
} else {
d3.select("#keep-data").attr("disabled", "disabled");
d3.select("#exclude-data").attr("disabled", "disabled");
};
function keep_data() {
new_data = actives();
if (new_data.length == 0) {
alert("I don't mean to be rude, but I can't let you remove all the data.\n\nTry removing some brushes to get your data back. Then click 'Keep' when you've selected data you want to look closer at.");
return false;
}
data = new_data;
rescale();
}
// Exclude selected from the dataset
function exclude_data() {
new_data = _.difference(data, actives());
if (new_data.length == 0) {
alert("I don't mean to be rude, but I can't let you remove all the data.\n\nTry selecting just a few data points then clicking 'Exclude'.");
return false;
}
data = new_data;
rescale();
}
</script>
It may be something with the if statement not getting information?

Related

How to clear canvas properties and events and add it back in fabricjs?

Context:
I have a use case where I need to get old canvas from the stack and assign it to the currently active canvas in the dom. The problem i am currently facing here is the canvas properties (e.g backgroundColor, height etc) gets replaced and shows up properly but in case of objects within the canvas the properties and events looks like its getting replaced but doesn't show up in the active canvas(DOM).
Note: I do not have option to use fabric's undo/redo here.
I have tried to recreate the issue in the snippet where I am trying to do the following.
Step 1: I am creating the canvas and objects with default properties using addTextBox method, once the default properties are applied I am storing the canvas and textbox object in a variable called as state for backup.
Step 2:
on AddHelloText Click(Button) I am pushing state which was recorded
in the previous step on to undo stack.
After that I am assigning "hello" to textbox and "changing background color
to red".
Step 3: on Undo Click(Button) I am getting the canvasOb from undoStack and assigning i to canvas (active canvas).
In this step the when i do undo, the values/events on the canvas are supposed to be of backup canvas which was stored in the undoStack,but instead it has incorrect values.
In simple words the canvas background is supposed to be "blue" and textbox object is supposed to have "Default text", even when i click
on textbox it is supposed to have background "blue" and text as
"Default text"
These are the two issues i am facing currently.
Only background gets updated and textobject remains the same.
On clicking bounding box (text box), it shows up a different instance of the canvas(the one with the step2 values)
Not sure how exactly i should proceed further.
Any suggestions would be really helpful.
var canvas = new fabric.Canvas("c");
var t1 = null;
var state = {};
const addTextBox=(canvasParam)=>{
t1 = new fabric.Textbox('Default text', {
width: 100,
top: 5,
left: 5,
fontSize: 16,
textAlign: 'center',
fill: '#ffffff',
});
canvas.on('selected', () => {
canvas.clearContext(canvas.contextTop);
});
canvas.setBackgroundColor('blue');
canvas.add(t1);
state={id:1,canvasOb:canvas,t1Backup:t1};
}
addTextBox(canvas);
var undoStack =[];
//Update the text to hello and change canvas background to red
$('#addHello').on('click', function() {
undoStack.push({state:_.cloneDeep(state)})
canvas.setBackgroundColor('red');
t1.text="hello red"
canvas.renderAll();
})
//apply previous canvas stored in the stack
$('#undo').on('click', function() {
console.log("TextBox value stored in undo stack: ",undoStack[0].state.canvasOb.getObjects()[0].text);
console.log("TextBox value stored in undo stack: ",undoStack[0].state.t1Backup.text);
canvas = undoStack[0].state.canvasOb;
console.log("Textbox value that should be displayed currently instead of 'hello red':",canvas.getObjects()[0].text);
canvas.renderAll();
})
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
}
#c {
box-shadow: 0 0 20px 0px #9090908a;
border-radius: 1rem;
//padding:.5rem;
}
#app-container{
height: 30vh;
width: 30vw;
}
<script src="https://unpkg.com/lodash#4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.1.0/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div >
<html><body><div id="app-container">
<canvas id="c" width="200" height="300"></canvas>
<button id="addHello" class="button">Add hello</button>
<button id="undo" class="button">Undo</button>
</div></body></html>
You are changing the canvas object on the fly even though you are saving its reference inside your state object. So as you update text or change the background color of canvas and etc..., you are in fact changing your saved state, because all you did so far is saving a reference to fabric.js canvas object.
This article can be interesting:
Reference vs Primitive values in Javascript
The best way to accomplish what you want to achieve (in my opinion) would be to use the fabric.js toJSON method and save the state as JSON, then retrieve it again using loadFromJSON whenever you need it.
Here's an example:
var canvas = new fabric.Canvas('c');
var t1 = null;
var undoStack = [];
// Whenever you make a change and then you want to save it as a state
const addNewState = () => {
var canvasObject = canvas.toJSON();
undoStack.push(JSON.stringify(canvasObject));
};
const initCanvas = () => {
t1 = new fabric.Textbox('Default text', {
width: 100,
top: 5,
left: 5,
fontSize: 16,
textAlign: 'center',
fill: '#ffffff',
});
canvas.on('selected', () => {
canvas.clearContext(canvas.contextTop);
});
canvas.setBackgroundColor('blue');
canvas.add(t1);
addNewState(); // Our first state
};
// Init canvas with default parameters
initCanvas(canvas);
const retrieveLastState = () => {
var latestState = undoStack[undoStack.length - 1]; // Get last state
var parsedJSON = JSON.parse(latestState);
canvas.loadFromJSON(parsedJSON);
};
// Update the text to hello and change canvas background to red
$('#addHello').on('click', function () {
canvas.setBackgroundColor('red');
t1.text = 'hello red';
canvas.renderAll();
});
// apply previous canvas stored in the stack
// Retrieve the very last state
$('#undo').on('click', function () {
retrieveLastState();
canvas.renderAll();
});
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
}
#c {
box-shadow: 0 0 20px 0px #9090908a;
border-radius: 1rem;
}
#app-container {
height: 30vh;
width: 30vw;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app-container">
<canvas id="c" width="200" height="300"></canvas>
<button id="addHello" class="button">Add hello</button>
<button id="undo" class="button">Undo</button>
</div>
<script src="https://unpkg.com/lodash#4/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.1.0/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</body>
</html>
However, the next time you click on #addHello button and then you try to undo it again, it's not going to work. The reason for that is we are trying to access t1 variable which is a fabric.js object on the very first canvas. The solution would be to assign an id to each object you add to the canvas so that you can retrieve them again (for modifying, deleting and etc...). This would make things a lot more complicated so I didn't put that in here.

FabricJS check if double clicked on a text object with _onDoubleClick?

I've been playing around with my fabric.min.js file recently and I came across this:
_onDoubleClick: function(t) {
this._cacheTransformEventData(t), this._handleEvent(t, "dblclick"), this._resetTransformEventData(t);
},
So instinctively, I decided to see what it did by adding an alert() function. As expected, it just showed the alert whenever the mouse was double-clicked.
What I'm trying to do is make a different alert for the object type, if that makes sense.
_onDoubleClick: function(t) {
this._cacheTransformEventData(t), this._handleEvent(t, "dblclick"), this._resetTransformEventData(t);
// This code doesn't work
if (t.type === 'text') alert("You double-clicked on a text box")
else alert("You double-clicked on a prop")
},
Basically, I just want to check if a text box was double-clicked or not, how can I do this?
Changing the library itself is normally never a good idea. FabricJS uses that function to give as a nice events interface:
http://fabricjs.com/events
http://fabricjs.com/fabric-intro-part-2#events
Please see the example below to see how can you achieve what you are looking for, without changing the core library.
var canvas = new fabric.Canvas(document.querySelector('canvas'))
var textBox = new fabric.Text('Lorem Ipsum Dolor', { left: 20, top: 20 })
var circle = new fabric.Circle({ radius: 30, fill: 'green', left: 130, top: 75 })
// Listen on the text object
textBox.on('mousedblclick', function() {
console.log('Text object was double clicked')
})
// Listen on the circle object
circle.on('mousedblclick', function() {
console.log('Circle object was double clicked')
})
// Listen for any double click events on the canvas
canvas.on('mouse:dblclick', function(e) {
if (e.target.type === 'circle') {
console.log('The clicked object was a circle')
} else if (e.target.type === 'text') {
console.log('The clicked object was a text')
}
})
canvas.add(textBox)
canvas.add(circle)
canvas.renderAll()
body {
background: #f0f0f0;
padding: 0;
margin: 0;
}
canvas {
border: 1px solid lightgray;
}
div {
margin: 0.5em 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.6.3/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas width="400" height="150"></canvas>

Plotly.js display tooltip on hover over graph title

Plotly.js allows you to specify a graph title, but there doesn't seem to be an option for specifying a longer description to be shown on hovering over the title.
So, I added a title attribute to the text element that encloses the title text, and then activated JQueryUI Tooltips on the page. But nothing seems to happen when I hover over the title. Here's how I added the title attribute and activated the tooltips:
$('div#myDiv g.g-gtitle > text.gtitle')[0].title = 'This is a long description that should show on hover over the title';
$( document ).tooltip();
I've also tried the following, which doesn't work either:
$('div#myDiv g.g-gtitle > text.gtitle').attr('title', 'This is a long description that should show on hover over the title');
$( document ).tooltip();
Full example code here: https://codepen.io/synaptik/pen/eKBYbE
Any idea how I can display a tooltip when hovering over the graph title?
I checked your code, Jquery-ui-tooltip is causing some problems when working together with plotly, I don't want to get into specifics, but basically, on hover a new element is getting added to the previous hover element.
This is my version of a solution for your question, plotly has set all the graph elements to pointer-events:none where no events will happen on those elements, so I disabled this using the CSS pointer-events: all, Also we need to wrap the graph inside a div and a span containing the contents of the tooltip, thus using javascript events mouseover and mouseout, we can hide/show the tooltip.
Please go through the below code and let me know if any doubts, also let me know if your issue is resolved, Thanks!
var data_x = ['2018-05-01', '2018-05-02', '2018-05-03', '2018-05-04', '2018-05-05', '2018-05-06', '2018-05-07', '2018-05-08', '2018-05-09'];
// data
var Data = {
type: 'scatter',
x: data_x,
y: [4, 2, -1, 4, -5, -7, 0, 3, 8],
mode: 'lines+markers',
name: 'Data',
showlegend: true,
hoverinfo: 'all',
line: {
color: 'blue',
width: 2
},
marker: {
color: 'blue',
size: 8,
symbol: 'circle'
}
}
// layout
var layout = {
title: 'My Title',
xaxis: {
zeroline: false
},
yaxis: {
range: [data_x[0], data_x[data_x.length - 1]],
zeroline: false
}
}
Plotly.plot('myDiv', [Data], layout);
$('#myDiv text.gtitle').on('mouseover', function(e) {
var hovertext = $('span.custom-tooltip');
var pos = e.target.getBoundingClientRect();
hovertext.css("left", (pos.left - (hovertext.width() / 2) + (pos.width / 2)) + "px");
hovertext.css("top", pos.top * 1.5 + "px");
hovertext.css("visibility", "visible");
})
$('#myDiv text.gtitle').on('mouseout', function(e) {
var hovertext = $('span.custom-tooltip');
hovertext.css("visibility", "hidden");
})
.wrapper {
position: relative;
}
.wrapper .custom-tooltip {
visibility: hidden;
display: inline;
width: fit-content;
position: absolute;
left: 0px;
right: 0px;
}
#myDiv text.gtitle {
pointer-events: all !important;
}
.wrapper .custom-tooltip.show {
visibility: visible;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<!-- Plotly chart will be drawn inside this DIV -->
<div class="wrapper">
<div id="myDiv"></div>
<span class="custom-tooltip">This is a really long title</span>
</div>
<script>
/* JAVASCRIPT CODE GOES HERE */
</script>
Instead of using JQuery Tooltips, I went with Tippy. However, I still had to modify the Plotly chart like this (thanks to #Naren-Murali):
$('div#myDiv g.g-gtitle > text.gtitle').addClass('graphWrapperPointerEvents');
with the CSS:
.graphWrapperPointerEvents {
pointer-events: all !important;
}
Solution:
https://codepen.io/synaptik/pen/LrWMKM

Uncaught TypeError: Cannot read property 'setProperty' of undefined

I tried to find an answer to this question all over the internet. I found one similar question, but it seems no one was able to give the right answer.
I was trying out an example from the Google Maps API website https://developers.google.com/maps/documentation/javascript/combining-data. I only made a few changes to your code in order to use census data for a different country.
I changed the links so that it would access my own json files. In region.geojson you have all the coordinates for Turkish provinces and in test.json you have the census data for each province.
I put the census data in the same format but when the loadCensusData function hits
map.data
.getFeatureById(stateId)
.setProperty('census_variable', censusVariable);
while iterating it gives me the following error, "Uncaught TypeError: Cannot read property 'setProperty' of undefined".
If I comment this out it will proceed the iteration until it ends but the problem is that I will not be able to use 'census_variable' as it has not been set.
4. If I change the links to point back to the US census data, it works again as expected.
Any ideas how I can solve this error? Or do you know what is causing this?
Please see the code for my map below:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<title>Mashups with google.maps.Data</title>
<style>
html, body, #map { height: 100%; margin: 0; padding: 0; overflow: hidden; }
.nicebox {
position: absolute;
text-align: center;
font-family: "Roboto", "Arial", sans-serif;
font-size: 13px;
z-index: 5;
box-shadow: 0 4px 6px -4px #333;
padding: 5px 10px;
background: rgb(255,255,255);
background: linear-gradient(to bottom,rgba(255,255,255,1) 0%,rgba(245,245,245,1) 100%);
border: rgb(229, 229, 229) 1px solid;
}
#controls {
top: 10px;
left: 110px;
width: 360px;
height: 45px;
}
#data-box {
top: 10px;
left: 500px;
height: 45px;
line-height: 45px;
display: none;
}
#census-variable {
width: 360px;
height: 20px;
}
#legend { display: flex; display: -webkit-box; padding-top: 7px }
.color-key {
background: linear-gradient(to right,
hsl(5, 69%, 54%) 0%,
hsl(29, 71%, 51%) 17%,
hsl(54, 74%, 47%) 33%,
hsl(78, 76%, 44%) 50%,
hsl(102, 78%, 41%) 67%,
hsl(127, 81%, 37%) 83%,
hsl(151, 83%, 34%) 100%);
flex: 1;
-webkit-box-flex: 1;
margin: 0 5px;
text-align: left;
font-size: 1.0em;
line-height: 1.0em;
}
#data-value { font-size: 2.0em; font-weight: bold }
#data-label { font-size: 2.0em; font-weight: normal; padding-right: 10px; }
#data-label:after { content: ':' }
#data-caret { margin-left: -5px; display: none; font-size: 14px; width: 14px}
</style>
</head>
<body>
<div id="controls" class="nicebox">
<div>
<select id="census-variable">
<option value="test">Fertility rate 2015</option>
<option value="https://storage.googleapis.com/mapsdevsite/json/DP05_0017E">Median age</option>
</select>
</div>
<div id="legend">
<div id="census-min">min</div>
<div class="color-key"><span id="data-caret">◆</span></div>
<div id="census-max">max</div>
</div>
</div>
<div id="data-box" class="nicebox">
<label id="data-label" for="data-value"></label>
<span id="data-value"></span>
</div>
<div id="map"></div>
<script>
var mapStyle = [{
'stylers': [{'visibility': 'off'}]
}, {
'featureType': 'landscape',
'elementType': 'geometry',
'stylers': [{'visibility': 'on'}, {'color': '#fcfcfc'}]
}, {
'featureType': 'water',
'elementType': 'geometry',
'stylers': [{'visibility': 'on'}, {'color': '#bfd4ff'}]
}];
var map;
var censusMin = Number.MAX_VALUE, censusMax = -Number.MAX_VALUE;
function initMap() {
// load the map
map = new google.maps.Map(document.getElementById('map'), {
center: {lat: 40, lng: -100},
zoom: 4,
styles: mapStyle
});
// set up the style rules and events for google.maps.Data
map.data.setStyle(styleFeature);
map.data.addListener('mouseover', mouseInToRegion);
map.data.addListener('mouseout', mouseOutOfRegion);
// wire up the button
var selectBox = document.getElementById('census-variable');
google.maps.event.addDomListener(selectBox, 'change', function() {
clearCensusData();
loadCensusData(selectBox.options[selectBox.selectedIndex].value);
});
// state polygons only need to be loaded once, do them now
loadMapShapes();
}
/** Loads the state boundary polygons from a GeoJSON source. */
function loadMapShapes() {
// load US state outline polygons from a GeoJson file
map.data.loadGeoJson('region.geojson', { idPropertyName: 'meso:name_local' });
// wait for the request to complete by listening for the first feature to be
// added
google.maps.event.addListenerOnce(map.data, 'addfeature', function() {
google.maps.event.trigger(document.getElementById('census-variable'),
'change');
});
}
/**
* Loads the census data from a simulated API call to the US Census API.
*
* #param {string} variable
*/
function loadCensusData(variable) {
// load the requested variable from the census API (using local copies)
var xhr = new XMLHttpRequest();
xhr.open('GET', variable + '.json');
xhr.onload = function() {
var censusData = JSON.parse(xhr.responseText);
censusData.shift(); // the first row contains column names
censusData.forEach(function(row) {
var censusVariable = parseFloat(row[0]);
var stateId = row[1];
console.log('StateId row[0] '+censusVariable);
console.log('censusVariable row[1] '+stateId);
// keep track of min and max values
if (censusVariable < censusMin) {
censusMin = censusVariable;
}
if (censusVariable > censusMax) {
censusMax = censusVariable;
}
// update the existing row with the new data
map.data
.getFeatureById(stateId)
.setProperty('census_variable');
});
// update and display the legend
document.getElementById('census-min').textContent =
censusMin.toLocaleString();
document.getElementById('census-max').textContent =
censusMax.toLocaleString();
};
xhr.send();
}
/** Removes census data from each shape on the map and resets the UI. */
function clearCensusData() {
censusMin = Number.MAX_VALUE;
censusMax = -Number.MAX_VALUE;
map.data.forEach(function(row) {
row.setProperty('census_variable', undefined);
});
document.getElementById('data-box').style.display = 'none';
document.getElementById('data-caret').style.display = 'none';
}
/**
* Applies a gradient style based on the 'census_variable' column.
* This is the callback passed to data.setStyle() and is called for each row in
* the data set. Check out the docs for Data.StylingFunction.
*
* #param {google.maps.Data.Feature} feature
*/
function styleFeature(feature) {
var low = [5, 69, 54]; // color of smallest datum
var high = [151, 83, 34]; // color of largest datum
// delta represents where the value sits between the min and max
var delta = (feature.getProperty('census_variable') - censusMin) /
(censusMax - censusMin);
var color = [];
for (var i = 0; i < 3; i++) {
// calculate an integer color based on the delta
color[i] = (high[i] - low[i]) * delta + low[i];
}
// determine whether to show this shape or not
var showRow = true;
if (feature.getProperty('census_variable') == null ||
isNaN(feature.getProperty('census_variable'))) {
showRow = false;
}
var outlineWeight = 0.5, zIndex = 1;
if (feature.getProperty('state') === 'hover') {
outlineWeight = zIndex = 2;
}
return {
strokeWeight: outlineWeight,
strokeColor: '#fff',
zIndex: zIndex,
fillColor: 'hsl(' + color[0] + ',' + color[1] + '%,' + color[2] + '%)',
fillOpacity: 0.75,
visible: showRow
};
}
/**
* Responds to the mouse-in event on a map shape (state).
*
* #param {?google.maps.MouseEvent} e
*/
function mouseInToRegion(e) {
// set the hover state so the setStyle function can change the border
e.feature.setProperty('state', 'hover');
var percent = (e.feature.getProperty('census_variable') - censusMin) /
(censusMax - censusMin) * 100;
// update the label
document.getElementById('data-label').textContent =
e.feature.getProperty('NAME');
document.getElementById('data-value').textContent =
e.feature.getProperty('census_variable').toLocaleString();
document.getElementById('data-box').style.display = 'block';
document.getElementById('data-caret').style.display = 'block';
document.getElementById('data-caret').style.paddingLeft = percent + '%';
}
/**
* Responds to the mouse-out event on a map shape (state).
*
* #param {?google.maps.MouseEvent} e
*/
function mouseOutOfRegion(e) {
// reset the hover state, returning the border to normal
e.feature.setProperty('state', 'normal');
}
</script>
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=API_KEY&callback=initMap">
</script>
</body>
</html>
Here is my JSON file with Census Data (test.json):
[["DP02_0066PE","region"],
["2.31", "Adana"],
["2.86", "Adıyaman"],
["2.06", "Afyonkarahisar"],
["3.80", "Ağrı"],
["2.24", "Aksaray"],
["1.79", "Amasya"],
["1.76", "Ankara"],
["1.96", "Antalya"],
["2.15", "Ardahan"],
["1.73", "Artvin"],
["1.85", "Aydın"],
["1.64", "Balıkesir"],
["1.58", "Bartın"],
["3.24", "Batman"],
["2.07", "Bayburt"],
["1.71", "Bilecik"],
["2.42", "Bingöl"],
["3.40", "Bitlis"],
["1.59", "Bolu"],
["1.77", "Burdur"],
["1.91", "Bursa"],
["1.53", "Çanakkale"],
["1.77", "Çankırı"],
["1.83", "Çorum"],
["1.84", "Denizli"],
["3.21", "Diyarbakır"],
["1.85", "Düzce"],
["1.53", "Edirne"],
["1.97", "Elazığ"],
["1.82", "Erzincan"],
["2.51", "Erzurum"],
["1.55", "Eskişehir"],
["3.15", "Gaziantep"],
["1.62", "Giresun"],
["1.66", "Gümüşhane"],
["2.69", "Hakkâri"],
["2.70", "Hatay"],
["3.04", "Iğdır"],
["1.70", "Isparta"],
["1.88", "İstanbul"],
["1.72", "İzmir"],
["2.61", "Kahramanmaraş"],
["1.56", "Karabük"],
["2.10", "Karaman"],
["2.64", "Kars"],
["1.62", "Kastamonu"],
["2.18", "Kayseri"],
["2.92", "Kilis"],
["1.71", "Kırıkkale"],
["1.54", "Kırklareli"],
["1.79", "Kırşehir"],
["2.06", "Kocaeli"],
["2.18", "Konya"],
["1.58", "Kütahya"],
["2.01", "Malatya"],
["1.92", "Manisa"],
["3.41", "Mardin"],
["2.15", "Mersin"],
["1.75", "Muğla"],
["3.45", "Muş"],
["1.96", "Nevşehir"],
["2.18", "Niğde"],
["1.81", "Ordu"],
["2.47", "Osmaniye"],
["1.78", "Rize"],
["1.92", "Sakarya"],
["1.80", "Samsun"],
["4.38", "Şanlıurfa"],
["3.55", "Siirt"],
["1.72", "Sinop"],
["4.01", "Şırnak"],
["1.96", "Sivas"],
["1.93", "Tekirdağ"],
["1.72", "Tokat"],
["1.85", "Trabzon"],
["1.69", "Tunceli"],
["1.73", "Uşak"],
["3.36", "Van"],
["1.77", "Yalova"],
["1.99", "Yozgat"],
["1.55", "Zonguldak"]]
The other file with all the geoJSON data is way too big to post here. But if necessary I can find a way to share it with you.
I finally figured it out. To be frank, it was a pretty stupid mistake from my part. I guess I was already tired and didn't realize my mistake so I'd like to thank everyone who commented on my question as it helped me realize what I did wrong.
There are two JSON files. One that contains the coordinates, let's call it coordinates.geojson and the other contains the census data, let's call it census.json.
Now for these lines of code to work,
map.data
.getFeatureById(stateId)
.setProperty('census_variable');
you must make sure that stateId corresponds to both the id in the census.json file as well as the id in the coordinates.geojson file.
In my case the stateId from the census.json file did not equal the first id/state in coordinates.geojson which was Aydin. Whereas census.json started with Adana, then Adıyaman etc.
Therefore I had to change the order of the census data to
[["DP02_0066PE","region"],
["1.85", "Aydin"],
["1.72", "Izmir"],
["1.64", "Balikesir"],
["1.53", "Çanakkale"],
["1.53", "Edirne"],
["1.54", "Kirklareli"],
["1.93", "Tekirdag"],
["1.71", "Bilecik"],
["1.91", "Bursa"],
["1.88", "Istanbul"],
["2.06", "Kocaeli"],
["1.92", "Sakarya"],
["1.59", "Bolu"],
["1.55", "Eskisehir"],
["1.62", "Kastamonu"],
["1.96", "Antalya"],
["2.06", "Afyon"],
["1.77", "Burdur"],
["1.84", "Denizli"],
["1.70", "Isparta"],
["1.58", "Kütahya"],
["1.92", "Manisa"],
["1.75", "Mugla"],
["2.86", "Adiyaman"],
["1.97", "Elazig"],
["2.61", "Kahramanmaras"],
["2.01", "Malatya"],
["2.15", "Içel"],
["1.79", "Kirsehir"],
["2.18", "Kayseri"],
["1.96", "Nevsehir"],
["2.70", "Hatay"],
["1.79", "Amasya"],
["1.83", "Çorum"],
["1.62", "Giresun"],
["1.81", "Ordu"],
["1.72", "Sinop"],
["1.96", "Sivas"],
["1.80", "Samsun"],
["1.72", "Tokat"],
["1.73", "Artvin"],
["2.51", "Erzurum"],
["1.82", "Erzincan"],
["1.78", "Rize"],
["1.85", "Trabzon"],
["3.80", "Agri"],
["2.42", "Bingöl"],
["3.21", "Diyarbakir"],
["3.45", "Mus"],
["3.40", "Bitlis"],
["1.76", "Ankara"],
["1.77", "Çankiri"],
["1.55", "Zonguldak"],
["2.18", "Konya"],
["2.10", "Karaman"],
["1.73", "Usak"],
["3.15", "Gaziantep"],
["4.38", "Sanliurfa"],
["2.31", "Adana"],
["1.71", "Kirikkale"],
["2.18", "Nigde"],
["2.24", "Aksaray"],
["1.99", "Yozgat"],
["1.66", "Gümüshane"],
["2.07", "Bayburt"],
["2.64", "Kars"],
["3.41", "Mardin"],
["3.24", "Batman"],
["3.55", "Siirt"],
["4.01", "Sirnak"],
["1.69", "Tunceli"],
["2.69", "Hakkari"],
["3.36", "Van"],
["2.15", "Ardahan"],
["3.04", "Igdir"],
["2.92", "Kilis"],
["2.47", "Osmaniye"],
["1.77", "Yalova"],
["1.85", "Düzce"],
["1.56", "Karabük"],
["1.58", "Bartin"]]
Last but not least, please note that I removed all the specific Turkish characters such as ğ, ı and ş because the id from coordinates.geojson where spelled without them. Hence I changed Ağrı to Agri and so forth.
I guess it would have been better had I used numbers as IDs instead of names.
I hope this will prevent other people of making the same mistake.

How can I dynamically add a pattern image to a svg?

I'm new working with SVG images. I'm using them to paint a jacket several times of different colors without needing to have an image for each colour. This is being done with jQuery.
That was the 'easy' part. I solved it by applying fill: #color; CSS rule to <path> inside <svg>.
The hard part is when trying to fill the svg path with an image. It's really weird. My code is printed just fine in the html inside the <svg>, but not working at all. And when, in chrome's dev tools, I cut the <defs> element, and paste it again exactly where it was, it suddenly works! It's driving me crazy :(.
My code:
var items = [
{
title: 'Africa',
// color: '#776254'
},
{
title: 'Aguamarina',
// color: '#9cd3be'
},
{
title: 'Aluminio',
// color: '#9b9b9b'
},
{
title: 'Amarillo Jamaica',
// color: '#ffcd01'
},
{
title: 'Amatista',
// color: '#4d4169'
},
{
title: 'Ambar Brillante',
// color: '#eb6608'
},
{
title: 'Arándano',
// color: '#604483'
}
];
$(function () {
var PrendaShow = {
$mac_wrapper: $('#prendas-mac-slider-wrapper .mac-slider'),
$fullprints_wrapper: $('#fullprints-gallery'),
img: {
src: 'image.svg',
width: 350,
height: 188
},
init: function () {
this.makeItems();
this.bindEvents();
},
bindEvents: function () {
// events
},
makeItems: function() {
var self = this,
$model = $('<div/>', { class: 'mac-item' });
$model.append($('<div/>', { class: 'item-img' }));
$.each(items, function() {
var $item = $model.clone();
self.$mac_wrapper.append($item);
});
this.svgDraw();
},
svgDraw: function () {
var self = this;
$.get(this.img.src, function(data) {
var $svg = self.normalizeSvg(data);
self.appendImgs($svg);
});
},
normalizeSvg: function (data) {
var $svg = $(data).find('svg');
$svg = $svg.removeAttr('xmlns:a');
$svg.width(this.img.width).height(this.img.height);
return $svg;
},
appendImgs: function ($svg) {
var items = this.$mac_wrapper.find('.mac-item');
$.each(items, function() {
var $clone = $svg.clone(),
color = $(this).data('color');
$clone.find('path').css('fill', 'url(#img1)');
var img = document.createElementNS('http://www.w3.org/2000/svg', 'image');
img.setAttributeNS(null,'height','536');
img.setAttributeNS(null,'width','536');
img.setAttributeNS('http://www.w3.org/1999/xlink','href','http://www.boogdesign.com/examples/svg/daisy-grass-repeating-background.jpg');
img.setAttributeNS(null,'x','10');
img.setAttributeNS(null,'y','10');
img.setAttributeNS(null, 'visibility', 'visible')
$clone.prepend($('<defs/>').html('<pattern id="img1" patternUnits="userSpaceOnUse" width="100" height="100"></pattern>'));
$clone.find('pattern').append(img)
$(this).find('.item-img').append($clone);
});
}
};
PrendaShow.init();
});
#prendas-mac-slider-wrapper {
margin-top: 80px;
}
.mac-slider {
display: flex;
font-size: 0;
padding: 0 100px;
}
.mac-item {
width: 100%;
height: 160px;
min-width: 0;
position: relative;
display: inline-block;
text-align: center;
cursor: pointer;
}
.item-img {
background: url('https://ae01.alicdn.com/kf/HTB1Cqi6KXXXXXbpXVXXq6xXFXXXy/Classic-font-b-White-b-font-font-b-Jacket-b-font-font-b-Men-b-font.jpg') center center no-repeat;
background-size: contain;
pointer-events: none;
position: absolute;
top: 0;
left: -9999px;
right: -9999px;
margin: auto;
}
svg {
mix-blend-mode: multiply;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="hero">
<div class="container-fluid">
<div id="prendas-mac-slider-wrapper" class="row">
<div class="mac-slider">
</div>
</div>
</div>
</div>
I'm not being able to give my exact example because of images upload. I really hope you understand what I'm trying to do here, and what's going wrong.
EDIT: Not sure if this deserves to be in an answer, but I preferred to write it here since I don't think its the right way to do this.
As I knew that, from chrome dev tools, if I deleted the <defs> element and pasted it again, it worked, I tried this and worked:
var svg_html = $svg.html();
$svg.html('');
$svg.html(svg_html);
Your problem is here:
$clone.prepend($('<defs/>').html('<pattern ...
You cannot use jQuery to create SVG elements like this. You are doing it the correct way (with createElementNS()) when you create the <image> element. But you also need to use the same method when creating the <defs> and <pattern> elements.

Categories