D3 : map with topojson doesn't render properly - javascript

I'm trying to draw svg map with d3 from topojson file, but all I got is messed up lines.
I'm using most of the code I found on http://www.tnoda.com/blog/2013-12-07. When I use topojson files from that site, everything works fine. I tought maybe the problem is in my topojson file, but when I import it in mapshaper, I get normal map.
plnkr: http://plnkr.co/edit/TYiT5AoI29nEHC3Fre6D?p=preview
Here is my code:
index.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css" type="text/css">
<script src="//code.jquery.com/jquery-2.0.0.js"></script>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
</head>
<body>
<div id="map"></div>
<script src="script.js"></script>
</body>
</html>
script.js
var m_width = $("#map").width(),
width = 800,
height = 500
var projection = d3.geo.mercator()
.scale(105)
.translate([width / 2, height / 1.5]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("#map").append("svg")
.attr("width", m_width)
.attr("height", m_width * height / width);
var g = svg.append("g");
d3.json("zupanije.max.topo.json", function(error, us) {
g.append("g")
.attr("id", "states")
.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter()
.append("path")
.attr("id", function(d) { return d.id; })
.attr("d", path)
});
styles.css
#map {
background-color: #fff;
border: 1px solid #ccc;
}
.background {
fill: none;
pointer-events: all;
}
#states{
cursor: pointer;
fill: #cde;
stroke: #fff;
stroke-linejoin: round;
stroke-linecap: round;
}
#states .active {
fill: #89a;
}
pre.prettyprint {
border: 1px solid #ccc;
margin-bottom: 0;
padding: 9.5px;
}

I was having the exact same problem and spent hours re-converting my SHP file to GeoJSON/Topojson in command line with different settings. The solution is quite simple!
- Get QGIS here: https://www.qgis.org/en/site/forusers/download.html
- open your SHP file or GeoJSON file
- Select the layer you want to export
- Go to Layer > Save as
- Format: Geojson
- CSR: WGS 84, EPSG: 4326
- Save.
Enjoy!

Related

How to change the names of provinces on the map?

I have a map created using D3 and JavaScript. I want to translate the names of Spain's provinces to another language, for example, to English. By default it is Spanish.
I would prefer to make these changes manually, however, I don't know which file should I edit. I tried to edit hdi.json and provincias.json, but it does not work (I get the provinces colored in black without any title, like it is not recognized).
Any help is highly appreciated.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.nombre{
stroke: #000;
stroke-width: 0.5px
}
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
}
.provinceNames
{
font-size: 0.9em;
font-family: "Lato";
}
.legendLinear
{
font-family: "Lato";
fill:#000000;
}
.legendTitle {
font-size: 1em;
}
#tooltip {
position: absolute;
top: 0;
left: 0;
z-index: 10;
margin: 0;
padding: 10px;
width: 200px;
height: 70px;
color: white;
font-family: sans-serif;
font-size: 1.0em;
font-weight: bold;
text-align: center;
background-color: rgba(0, 0, 0, 0.55);
opacity: 0;
pointer-events: none;
border-radius:5px;
transition: .2s;
}
</style>
<body>
<div id="container">
<div id="tooltip">
</div>
</div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.7.0/d3-legend.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-composite-projections/0.3.5/conicConformalSpain-proj.min.js"></script>
<script>
var width = 1000,
height = 800;
var projection = d3.geo.conicConformalSpain().scale(width*5).translate([200+width/2, 100+height/2]);
var graticule = d3.geo.graticule().step([2, 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("#container").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
//var g = svg.append("g");
d3.json("provincias.json", function(error, provincias) {
d3.json("hdi.json", function(error, hdi) {
var land = topojson.feature(provincias, provincias.objects.provincias);
var color = d3.scale.threshold()
.domain([1, 10, 100, 1000, 10000, 100000, 300000])
.range(["#feebe2","#e5d1ff","#ba93ef", "#8D4CE5","#6100E5","#4d00b7","#C94D8C"]);
svg.selectAll(".nombre")
.data(land.features)
.enter()
.append("path")
.attr("d", path)
.attr("class","nombre")
.style("fill",function(d){ return color(hdi[d.properties.nombre]) })
.on("mouseover", function(d){
//Show the tooltip
var x = d3.event.pageX;
var y = d3.event.pageY - 40;
d3.select("#tooltip")
.style("left", x + "px")
.style("top", y + "px")
.style("opacity", 1)
.text( "... " + d.properties.nombre + " ... " + hdi[d.properties.nombre]);
})
.on("mouseout", function(){
//Hide the tooltip
d3.select("#tooltip")
.style("opacity", 0);
});
svg
.append("path")
.style("fill","none")
.style("stroke","#000")
.attr("d", projection.getCompositionBorders());
svg.append("g")
.attr("class", "provinceNames")
.selectAll("text")
.data(land.features)
.enter()
.append("svg:text")
.text(function(d){
return d.properties.nombre;
})
.attr("x", function(d){
return path.centroid(d)[0];
})
.attr("y", function(d){
return path.centroid(d)[1];
})
.attr("text-anchor","middle")
.attr('fill', 'black');
d3.select("svg").append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(240,700)");
var logLegend = d3.legend.color()
.title("...")
.shapeHeight(20)
.shapeWidth(90)
.shapeRadius(10)
.labels([0, 10, 100, 1000, 10000, 100000, 300000])
.orient("horizontal")
.labelFormat(d3.format(".00f"))
.labelAlign("start")
.scale(color);
svg.select(".legendLinear")
.call(logLegend);
});
});
</script>
It seems that you're using this JSON for the provinces in Spain.
If that's correct, the file is "provincias.json" and this is the path for the names:
provincias.objects.provincias.geometries[index].properties.nombre
Where index is the index you want in the geometries array.
Check this demo:
d3.json("https://cdn.rawgit.com/rveciana/5919944/raw//provincias.json", function(provincias) {
provincias.objects.provincias.geometries.forEach(function(d) {
console.log(d.properties.nombre)
})
})
<script src="https://d3js.org/d3.v4.min.js"></script>

Loading a NY map

my d3 map of ny will not load. is there any way someone can help?
here is my code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 12px sans-serif;
}
path {
stroke-width: 1.75px;
stroke: #531b93;
fill: #919191;
cursor: pointer;
}
path:hover, path.highlighted {
fill: #0096ff;
}
div.tooltip {
position: absolute;
background-color: white;
border: 1px solid black;
color: black;
font-weight: bold;
padding: 4px 8px;
display: none;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
//Map dimensions (in pixels)
var width = 600,
height = 600;
//Map projection
var projection = d3.geo.mercator()
.scale(58722.369041340586)
.center([-73.97768078496284,40.705833704252484]) //projection center
.translate([width/2,height/2]) //translate to center the map in view
//Generate paths based on projection
var path = d3.geo.path()
.projection(projection);
//Create an SVG
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//Group for the map features
var features = svg.append("g")
.attr("class","features");
//Create zoom/pan listener
//Change [1,Infinity] to adjust the min/max zoom scale
var zoom = d3.behavior.zoom()
.scaleExtent([1, Infinity])
.on("zoom",zoomed);
svg.call(zoom);
//Create a tooltip, hidden at the start
var tooltip = d3.select("body").append("div").attr("class","tooltip");
d3.json("NYC_MapInfos.geojson",function(error,geodata) {
if (error) return console.log(error); //unknown error, check the console
//Create a path for each map feature in the data
features.selectAll("path")
.data(geodata.features)
.enter()
.append("path")
.attr("d",path)
.on("mouseover",showTooltip)
.on("mousemove",moveTooltip)
.on("mouseout",hideTooltip)
.on("click",clicked);
});
// Add optional onClick events for features here
// d.properties contains the attributes (e.g. d.properties.name, d.properties.population)
function clicked(d,i) {
}
//Update map on zoom/pan
function zoomed() {
features.attr("transform", "translate(" + zoom.translate() + ")scale(" + zoom.scale() + ")")
.selectAll("path").style("stroke-width", 1.75 / zoom.scale() + "px" );
}
//Position of the tooltip relative to the cursor
var tooltipOffset = {x: 5, y: -25};
//Create a tooltip, hidden at the start
function showTooltip(d) {
moveTooltip();
tooltip.style("display","block")
.text(d.properties.PO_NAME);
}
//Move the tooltip to track the mouse
function moveTooltip() {
tooltip.style("top",(d3.event.pageY+tooltipOffset.y)+"px")
.style("left",(d3.event.pageX+tooltipOffset.x)+"px");
}
//Create a tooltip, hidden at the start
function hideTooltip() {
tooltip.style("display","none");
}
</script>
here is the the geojson file: http://data.beta.nyc//dataset/3bf5fb73-edb5-4b05-bb29-7c95f4a727fc/resource/6df127b1-6d04-4bb7-b983-07402a2c3f90/download/f4129d9aa6dd4281bc98d0f701629b76nyczipcodetabulationareas.geojson
You can try geojson2svg module for SVG map creation with geojson data. As this is just plain JavaScript you'll have more control. Here is your example
Your html page (index.html):
<html>
<head>
<link rel="stylesheet" href="./map.css"/>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/geojson2svg/1.0.3/geojson2svg.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0-beta1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/geosquare/reproject-spherical-mercator/v0.1.3/dist/reproject-spherical-mercator.min.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/geosquare/geojson-bbox/master/dist/geojson-bbox.min.js"></script>
</head>
<body>
<h2>Example of New York postal code map created with geojson2svg</h2>
<div id="mapArea" style="width:600px;height:600px;">
<svg id="map" xmlns="http://www.w3.org/2000/svg"
width="600" height="600" x="0" y="0" >
</svg>
<div class="tooltip" ></div>
</div>
<script type="text/javascript" src="./main.js"></script>
</body>
Javascript code (main.js):
var dataURI = "http://data.beta.nyc//dataset/3bf5fb73-edb5-4b05-bb29-7c95f4a727fc/resource/6df127b1-6d04-4bb7-b983-07402a2c3f90/download/f4129d9aa6dd4281bc98d0f701629b76nyczipcodetabulationareas.geojson";
$.get(dataURI,drawGeoJSON);
$('#map').on('mouseover','path',function(ev) {
console.log(ev.target.feature.properties.postalCode);
var tooltip = document.querySelector('.tooltip');
tooltip.style.top = ev.pageY - 30;
tooltip.style.left = ev.pageX + 5;
tooltip.style.display = 'block';
tooltip.innerHTML = ev.target.feature.properties.PO_NAME;
});
$('#map').on('mouseout','path',function(ev) {
var tooltip = document.querySelector('.tooltip');
tooltip.style.display = 'none';
});
function drawGeoJSON(geojson) {
// covert wgs84 data to Web Mercator projection
var geojsonMerc = reproject(geojson);
// reproject: https://github.com/geosquare/reproject-spherical-mercator
var extent = bbox(geojsonMerc);
// bbox: https://github.com/geosquare/geojson-bbox
var mapExtent = {
left: extent[0],
bottom: extent[1],
right: extent[2],
top: extent[3]
};
var svgMap = document.getElementById('map');
// geojson2svg: https://github.com/gagan-bansal/geojson2svg
var convertor = geojson2svg(
{
viewportSize: {width:600,height:600},
mapExtent: mapExtent,
attributes: {
'vector-effect':'non-scaling-stroke'
},
explode: false
}
);
geojsonMerc.features.forEach(function(f) {
var svgStr = convertor.convert(f,{attributes: {id: 'pc-'+f.properties.OBJECTID}});
var svg = parseSVG(svgStr);
svgMap.appendChild(parseSVG(svgStr));
var svgEle = svgMap.querySelector('#' + 'pc-'+f.properties.OBJECTID);
svgEle.feature = f;
});
}
//parseSVG from http://stackoverflow.com/questions/3642035/jquerys-append-not-working-with-svg-element
function parseSVG(s) {
var div= document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
div.innerHTML= '<svg xmlns="http://www.w3.org/2000/svg">'+s+'</svg>';
var frag= document.createDocumentFragment();
while (div.firstChild.firstChild)
frag.appendChild(div.firstChild.firstChild);
return frag;
}
and CSS (map.css):
body {
font: 12px sans-serif;
}
path {
stroke-width: 1px;
stroke: #531b93;
fill: #919191;
cursor: pointer;
}
path:hover, path.highlighted {
fill: #0096ff;
}
div.tooltip {
position: absolute;
background-color: white;
border: 1px solid black;
color: black;
font-weight: bold;
padding: 4px 8px;
display: none;
}
For advance usage please check this blog

rails not registering javascript files under app/assets/javascripts

I'm currently trying to integrate d3 with my rails app. After installing the d3-rails gem, I came across this tutorial on d3. Somewhere in the tutorial, the below code is provided
<meta charset="utf-8">
<style>
.chart div {
font: 10px sans-serif;
background-color: steelblue;
text-align: right;
padding: 3px;
margin: 1px;
color: white;
}
</style>
<div class="chart"></div>
<script>
var data = [4, 8, 15, 16, 23, 42];
var x = d3.scale.linear()
.domain([0, d3.max(data)])
.range([0, 420]);
d3.select(".chart")
.selectAll("div")
.data(data)
.enter().append("div")
.style("width", function(d) { return x(d) + "px"; })
.text(function(d) { return d; });
</script>
If I were to include the above code as it is into my new.html.erb file, the chart will show.
I then created a new graph.js fiel under app/assets/javascripts and decided to put everything between the <script> </script> tag inside graph.js. Now when I open up new.html.erb, the chart will not show anymore.
Am I doing something wrong here that's causing rails to not recognize graph.js ?
Did you compile the assets?
You'll need to run:
rake asset:precompile

Searchable fiter for d3 donut charts

Hello I have a D3 script that outputs a bunch of donut charts and I am looking for a way to filter and search out keywords in the name of the first columns of my data. I would like to be able to do this live and am not looking for a way to filter the data specifically just but a search bar or some other way to allow users to search and filter through the donut charts. My biggest problem is incorporating the search element into the d3.csv() part I understand how to make a searchbar and have incorporated an example of one that I would like to be able to search through my data. I have done more research and perhaps using jquery would help.
Here is my D3:
<!DOCTYPE html>
<html>
<head>
<title>Search Box Example 1</title>
<meta name="ROBOTS" content="NOINDEX, NOFOLLOW" />
<!-- CSS styles for standard search box -->
<style type="text/css">
#tfheader{
background-color:#c3dfef;
}
#tfnewsearch{
float:right;
padding:20px;
}
.tftextinput{
margin: 0;
padding: 5px 15px;
font-family: Arial, Helvetica, sans-serif;
font-size:14px;
border:1px solid #0076a3; border-right:0px;
border-top-left-radius: 5px 5px;
border-bottom-left-radius: 5px 5px;
}
.tfbutton {
margin: 0;
padding: 5px 15px;
font-family: Arial, Helvetica, sans-serif;
font-size:14px;
outline: none;
cursor: pointer;
text-align: center;
text-decoration: none;
color: #ffffff;
border: solid 1px #0076a3; border-right:0px;
background: #0095cd;
background: -webkit-gradient(linear, left top, left bottom, from(#00adee), to(#0078a5));
background: -moz-linear-gradient(top, #00adee, #0078a5);
border-top-right-radius: 5px 5px;
border-bottom-right-radius: 5px 5px;
}
.tfbutton:hover {
text-decoration: none;
background: #007ead;
background: -webkit-gradient(linear, left top, left bottom, from(#0095cc), to(#00678e));
background: -moz-linear-gradient(top, #0095cc, #00678e);
}
/* Fixes submit button height problem in Firefox */
.tfbutton::-moz-focus-inner {
border: 0;
}
.tfclear{
clear:both;
}
</style>
</head>
<body>
<!-- HTML for SEARCH BAR -->
<div id="tfheader">
<form id="tfnewsearch" method="get" action="http://www.google.com">
<input type="text" class="tftextinput" name="q" size="21" maxlength="120"><input type="submit" value="search" class="tfbutton">
</form>
<div class="tfclear"></div>
</div>
</body>
</html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
svg {
padding: 10px 0 0 10px;
}
.arc {
stroke: #fff;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var radius = 74,
padding = 10;
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#2B8429"]);
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - 30);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
d3.csv("data1.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "device"; }));
data.forEach(function(d) {
d.ages = color.domain().map(function(name) {
return {name: name, population: +d[name]};
});
});
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", radius * 2)
.attr("height", radius * 2)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function(d) { return d; });
var svg = d3.select("body").selectAll(".pie")
.data(data)
.enter().append("svg")
.attr("class", "pie")
.attr("width", radius * 2)
.attr("height", radius * 2)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
svg.selectAll(".arc")
.data(function(d) { return pie(d.ages); })
.enter().append("path")
.attr("class", "arc")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.name); });
svg.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.device; });
var peopleTable = tabulate(data, ["device"]);
});
</script>
Here is some example data:
State,Under 5 Years,5 to 13 Years,14 to 17 Years,18 to 24 Years,25 to 44 Years,45 to 64 Years,65 Years and Over
AL,310504,552339,259034,450818,1231572,1215966,641667
AK,52083,85640,42153,74257,198724,183159,50277
AZ,515910,828669,362642,601943,1804762,1523681,862573
AR,202070,343207,157204,264160,754420,727124,407205
CA,2704659,4499890,2159981,3853788,10604510,8819342,4114496
CO,358280,587154,261701,466194,1464939,1290094,511094
CT,211637,403658,196918,325110,916955,968967,478007
DE,59319,99496,47414,84464,230183,230528,121688
DC,36352,50439,25225,75569,193557,140043,70648
FL,1140516,1938695,925060,1607297,4782119,4746856,3187797
GA,740521,1250460,557860,919876,2846985,2389018,981024
HI,87207,134025,64011,124834,356237,331817,190067
ID,121746,201192,89702,147606,406247,375173,182150
IL,894368,1558919,725973,1311479,3596343,3239173,1575308
IN,443089,780199,361393,605863,1724528,1647881,813839
IA,201321,345409,165883,306398,750505,788485,444554
KS,202529,342134,155822,293114,728166,713663,366706
KY,284601,493536,229927,381394,1179637,1134283,565867
I have seen examples of how to filter by set values but would like to have a search bar. Keep in mind I am relatively new to D3 and JS.

adding tooltips to pie chart using d3.js

I am embarking on a journey to learn to visualize data using d3.js, and so far I am finding the "Interactive Data Visualization" by Scott Murray very helpful. I was following through some of the example codes in book chapter 11, and was wondering how I would add the tooltip to the pie chart (the book already describes this procedure using the bar chart). Anyways, just been tinkering around with the codes for past couple of hours and would like to see if anyone can lend me a hand on this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: Pie layout</title>
<script type="text/javascript" src="d3/d3.v3.js"></script>
<style type="text/css">
text {
font-family: sans-serif;
font-size: 12px;
fill: white;
}
#tooltip {
position: absolute;
width: 200px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-mox-box-shadow: 4px 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rbga(0, 0, 0, 0.4)
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
</style>
</head>
<body>
<div id="tooltip" class="hidden">
<p><strong>Important Label Heading</strong></p>
<p><span id="value">100</span>%</p>
</div>
<script type="text/javascript">
//Width and height
var w = 300;
var h = 300;
var dataset = [ 5, 10, 20, 45, 6, 25 ];
var outerRadius = w / 2;
var innerRadius = 0;
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var pie = d3.layout.pie();
// Easy colors accessible via a 10-step ordinal scale
var color = d3.scale.category10();
// Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
// Set up groups
var arcs = svg.selectAll("g.arc")
.data(pie(dataset))
.enter()
.append("g")
.attr("class", "arc")
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")")
.on("mouseover", function(d){
d3.select("#tooltip")
.select("#value")
.text(d);
d3.select("tooltip").classed("hidden",false);
})
.on("mouseout", function() {
// Hide the tooltip
d3.select("#tooltip").classed("hidden", true);
});
// Draw arc paths
arcs.append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
// Labels
arcs.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.text(function(d) {
return d.value;
});
</script>
</body>
</html>
I know this is bit to digest, but what I want to know more specifically is how to set the x and y value for the tool-tip. Thank you in advance.
I prefer to use the opacity to show/hide the tooltip. Here is the FIDDLE. This should get you going.
d3.select("#tooltip")
.style("left", d3.event.pageX + "px")
.style("top", d3.event.pageY + "px")
.style("opacity", 1)
.select("#value")
.text(d.value);
I'm adding mouse move event on FernOfTheAndes's answer, This will makes it more pretty usecase. Hope this will be helpful
.on("mouseover", function(d) {
d3.select("#tooltip").style('opacity', 1)
.select("#value").text(d.value);
})
.on("mousemove", function(d) {
d3.select("#tooltip").style("top", (d3.event.pageY - 10) + "px")
.style("left", (d3.event.pageX + 10) + "px");
})
.on("mouseout", function() {
d3.select("#tooltip").style('opacity', 0);
});

Categories