I am trying to create a neural net visualization using d3.js in Angular 7. I have successfully created the nodes but the links are not appearing. The code refers to a css class defined in the components css file. What am I doing wrong?
Shown below is the code responsible for link creation:
// draw links
var link:any = svg.selectAll(".link")
.data(links)
.enter().append("line")
.attr("class", "link")
.attr("x1", function(d) { return nodes[d.source].x; })
.attr("y1", function(d) { return nodes[d.source].y; })
.attr("x2", function(d) { return nodes[d.target].x; })
.attr("y2", function(d) { return nodes[d.target].y; })
.style("stroke-width", function(d) {return Math.sqrt(d.value); });
css :
.link {
stroke: #999;
stroke-opacity: .6;
}
shown below is the code of my complete neural.component.ts file (which contains the above typescript code).
import { Component, OnInit,Input } from '#angular/core';
import {select,schemeCategory10,scaleOrdinal} from 'd3';
import { angularMath } from 'angular-ts-math';
declare var $:any;
#Component({
selector: 'app-neuralcanvas',
templateUrl: './neuralcanvas.component.html',
styleUrls: ['./neuralcanvas.component.css']
})
export class NeuralcanvasComponent implements OnInit {
// color = scaleOrdinal().range(schemeCategory10)
inputLayerHeight = 4;
outputLayerHeight=5;
hiddenLayersDepths =[3,4];
hiddenLayersCount =2;
nodeSize = 17;
width :any = 500 ;
height = 400;
constructor() { }
ngOnInit() {
this.draw()
}
draw() {
console.log('in draw')
if (!select("svg")[0]) {
} else {
//clear d3
select('svg').remove();
}
var svg = select("#neuralNet").append("svg")
.attr("width", this.width)
.attr("height", this.height);
var networkGraph : any = this.buildNodeGraph();
//buildNodeGraph();
this.drawGraph(networkGraph, svg);
}
buildNodeGraph() {
var newGraph:any = {
"nodes": []
};
//construct input layer
var newFirstLayer: any = [];
for (var i = 0; i < this.inputLayerHeight; i++) {
var newTempLayer1 :any = {"label": "i"+i, "layer": 1};
newFirstLayer.push(newTempLayer1);
}
//construct hidden layers
var hiddenLayers:any = [];
for (var hiddenLayerLoop = 0; hiddenLayerLoop < this.hiddenLayersCount; hiddenLayerLoop++) {
var newHiddenLayer:any = [];
//for the height of this hidden layer
for (var i = 0; i < this.hiddenLayersDepths[hiddenLayerLoop]; i++) {
var newTempLayer2:any = {"label": "h"+ hiddenLayerLoop + i, "layer": (hiddenLayerLoop+2)};
newHiddenLayer.push(newTempLayer2);
}
hiddenLayers.push(newHiddenLayer);
}
//construct output layer
var newOutputLayer:any = [];
for (var i = 0; i < this.outputLayerHeight; i++) {
var newTempLayer3 = {"label": "o"+i, "layer": this.hiddenLayersCount + 2};
newOutputLayer.push(newTempLayer3);
}
//add to newGraph
var allMiddle:any = newGraph.nodes.concat.apply([], hiddenLayers);
newGraph.nodes = newGraph.nodes.concat(newFirstLayer, allMiddle, newOutputLayer );
return newGraph;
}
drawGraph(networkGraph, svg) {
var color = scaleOrdinal(schemeCategory10);
var graph = networkGraph;
var nodes = graph.nodes;
// get network size
var netsize = {};
nodes.forEach(function (d) {
if(d.layer in netsize) {
netsize[d.layer] += 1;
} else {
netsize[d.layer] = 1;
}
d["lidx"] = netsize[d.layer];
});
// calc distances between nodes
var largestLayerSize = Math.max.apply(
null, Object.keys(netsize).map(function (i) { return netsize[i]; }));
var xdist = this.width / Object.keys(netsize).length,
ydist = (this.height-15) / largestLayerSize;
// create node locations
nodes.map(function(d) {
d["x"] = (d.layer - 0.5) * xdist;
d["y"] = ( ( (d.lidx - 0.5) + ((largestLayerSize - netsize[d.layer]) /2 ) ) * ydist )+10 ;
});
// autogenerate links
var links:any = [];
nodes.map(function(d, i) {
for (var n in nodes) {
if (d.layer + 1 == nodes[n].layer) {
links.push({"source": parseInt(i), "target": parseInt(n), "value": 1}) }
}
}).filter(function(d) { return typeof d !== "undefined"; });
// draw links
var link:any = svg.selectAll(".link")
.data(links)
.enter().append("line")
.attr("class", "link")
.attr("x1", function(d) { return nodes[d.source].x; })
.attr("y1", function(d) { return nodes[d.source].y; })
.attr("x2", function(d) { return nodes[d.target].x; })
.attr("y2", function(d) { return nodes[d.target].y; })
.style("stroke-width", function(d) {return Math.sqrt(d.value); });
// draw nodes
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; }
);
var circle = node.append("circle")
.attr("class", "node")
.attr("r", this.nodeSize)
.style("fill", function(d) { return color(d.layer); });
node.append("text")
.attr("dx", "-.35em")
.attr("dy", ".35em")
.attr("font-size", ".6em")
.text(function(d) { return d.label; });
}
}
the code of the neural.component.css:
.link {
stroke: #999;
stroke-opacity: .6;
}
The current output lokks like this:
I want to show the inks as:
As you'll see the generation code is already there I want to know how to refer the class to get the links appearing in Angular 7
Related
Need suggestions on how to add Curved Polyline using Here Maps Javascript API ?
There is no any straight way, but you can use following formula:
var D2R = Math.PI / 180;
var R2D = 180 / Math.PI;
var Coord = function(lon, lat) {
this.lon = lon;
this.lat = lat;
this.x = D2R * lon;
this.y = D2R * lat;
};
Coord.prototype.view = function() {
return String(this.lon).slice(0, 4) + ',' + String(this.lat).slice(0, 4);
};
Coord.prototype.antipode = function() {
var anti_lat = -1 * this.lat;
var anti_lon = (this.lon < 0) ? 180 + this.lon : (180 - this.lon) * -1;
return new Coord(anti_lon, anti_lat);
};
var LineString = function() {
this.coords = [];
this.length = 0;
};
LineString.prototype.move_to = function(coord) {
this.length++;
this.coords.push(coord);
};
var Arc = function(properties) {
this.properties = properties || {};
this.geometries = [];
};
Arc.prototype.json = function() {
if (this.geometries.length <= 0) {
return { 'geometry': { 'type': 'LineString', 'coordinates': null }, 'type': 'Feature', 'properties': this.properties };
} else if (this.geometries.length == 1) {
return { 'geometry': { 'type': 'LineString', 'coordinates': this.geometries[0].coords }, 'type': 'Feature', 'properties': this.properties };
} else {
var multiline = [];
for (var i = 0; i < this.geometries.length; i++) {
multiline.push(this.geometries[i].coords);
}
return { 'geometry': { 'type': 'MultiLineString', 'coordinates': multiline }, 'type': 'Feature', 'properties': this.properties };
}
};
Arc.prototype.strip = function() {
var s = H.geo.Strip ? new H.geo.Strip() : new H.geo.LineString();
for (var i = 0; i < this.geometries.length; i++) {
if (this.geometries[i].coords.lenght !== 0) {
var coords = this.geometries[i].coords;
for (var j = 0; j < coords.length; j++) {
var p = new H.geo.Point(coords[j][1], coords[j][0]);
s.pushPoint(p);
}
}
}
return s;
}
For detailed example, I have created a sample, please check - https://demo.support.here.com/examples/v3.1/geodesic_polyline
Default icons not showing for MarkerCluster plugin after using IconCreateFunction.
I want to use the default icons for the plugin but when using attached code I loose all the icons functions, I only get the numbers with no icons and if I activate the "childCount" I get one type of circle with the numbers offcenter within the icon. The markers has already been clustered and I want to add this value to the markercluster that is why I'm using the IconCreateFuncton so the numbers on the map shows correctly but I have lost all the icons and its beautiful functions... what is missing?
Result below using "var childCount"
$.getJSON("../test/test.geojson", function(json) {
geoLayer = L.geoJson(json, {
pointToLayer: function(feature, latlng) {
var log_p = feature.properties.log_p;
var marker;
if (log_p > 0){
marker = new L.shapeMarker(latlng, {radius: log_p*25, fillColor: '#2b83ba', fillOpacity: 0.5, color: '#000000', weight: 1, shape: 'circle'});
}
else {
marker = null
}
return marker;
},
onEachFeature: function(feature, layer) {
var popupText = "Amount per day: " + '<b>' + feature.properties.total + '</b>';
layer.bindPopup(popupText, {
closeButton: true,
offset: L.point(0, -20)
});
layer.on('click', function() {
layer.openPopup();
});
},
});
var markers = new L.MarkerClusterGroup({
iconCreateFunction: function(cluster) {
var children = cluster.getAllChildMarkers();
var sum = 0;
for (var i = 0; i < children.length; i++) {
sum += children[i].feature.properties.total;
}
/*
var childCount = cluster.getAllChildMarkers();
var c = ' marker-cluster-';
if (childCount < 10) {
c += 'small';
} else if (childCount < 500) {
c += 'medium';
} else {
c += 'large';
}
*/
return new L.DivIcon({ html: '<b>' + sum + '</b>', className: 'marker-cluster'/* + c */, iconSize: new L.Point(40, 40) });
}
});
markers.addLayer(geoLayer)
map.addLayer(markers);
});
Markercluster icons, styles and functions are lost
I manage to solve the problem, a few lines of code was missing. I added them to the original JavaScript code as follows.
$.getJSON("../test/test.geojson", function(json) {
geoLayer = L.geoJson(json, {
pointToLayer: function(feature, latlng) {
var log_p = feature.properties.log_p;
var marker;
if (log_p > 0) {
marker = new L.shapeMarker(latlng, {
radius: log_p * 25,
fillColor: '#2b83ba',
fillOpacity: 0.5,
color: '#000000',
weight: 1,
shape: 'circle'
});
} else {
marker = null
}
return marker;
},
onEachFeature: function(feature, layer) {
var popupText = "Amount per day: " + '<b>' + feature.properties.total + '</b>';
layer.bindPopup(popupText, {
closeButton: true,
offset: L.point(0, -20)
});
layer.on('click', function() {
layer.openPopup();
});
},
});
var clusters = new L.MarkerClusterGroup({
maxClusterRadius: 125,
iconCreateFunction: function(cluster) {
var children = cluster.getAllChildMarkers();
var sum = 0;
for (var i = 0; i < children.length; i++) {
sum += children[i].feature.properties.total;
}
var childCount = cluster.getChildCount()
var c = ' marker-cluster-';
if (childCount + sum <= 50) {
c += 'small';
} else if (childCount + sum <= 250) {
c += 'medium';
} else {
c += 'large';
}
return new L.DivIcon({
html: '<div><span>' + sum + '</span></div>',
className: 'marker-cluster marker-cluster-' + c,
iconSize: new L.Point(40, 40)
});
},
});
clusters.addLayer(geoLayer)
map.addLayer(clusters);
});
I would like to add a background image for each circle of my bubble chart. I already read some solutions on different subject but it doesn't solve my problem. All the other style are correctly added but not the background-image style.
scope.chart = function(rootData){
var diameter = 900,
format = d3.format(",d"),
color = d3.scale.category20c();
var bubble = d3.layout.pack()
.sort(null)
.size([diameter, diameter])
.value(function(d) { return (d.size); })
.padding(1.5);
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.attr("id","svg")
.attr("class", "bubble");
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(rootData))
.filter(function (d) {
return !d.children;
}))
.enter().append("g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.append("title")
.text(function(d) { return d.className + ": " + format(d.value); });
node.append("circle")
.attr("r", function (d) {
return d.r;
})
.style("opacity", "0")
.style("fill", function (d) {
return "black"
})
.style("background-image", function (d){
return "myImage2.png"
});
node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) { return d.className.substring(0, d.r / 3); })
.style("opacity", "0");
node.on("click", click);
d3.selectAll("circle").style("fill", function (d) {
return "black"
});
d3.select(this).select("circle").style('fill', '#f2f40d');
d3.selectAll("circle").style("filter", null);
d3.select(this).select("circle").style("filter", "url(#f1)");
}
// Returns a flattened hierarchy containing all leaf nodes under the root.
function classes(root) {
var classes = [];
function recurse(name, node) {
if (node.children) node.children.forEach(function (child) {
recurse(node.name, child);
});
else classes.push({
parameter1: name,
className: node.name,
size: node.size,
});
}
recurse(null, root);
return {children: classes};
}
d3.select(self.frameElement).style("height", diameter + "px");
}
Edit : This is the code I tried but the image not appears.
/**
* Create the bubble chart graph
* #constructor
*/
d3DemoApp.directive('chart', [function($scope) {
return {
restrict: 'EA',
transclude: true,
scope: {
parameter1: '=parameter1',
paramete2: '=paramete2',
paramete3: '=paramete3',
paramete4: '=paramete4',
paramete5: '=paramete5',
chartData : '=',
myFunction : '=myFunction'
},
link: function(scope) {
scope.$watch('chartData', function(newValue) {
if (newValue){
scope.chart(newValue);
}
});
scope.chart = function(rootData){
d3.select('svg').remove(); // This delete the graph not wanted
d3.select('svg').remove(); // This delete the graph not wanted
var diameter = 900,
format = d3.format(",d"),
color = d3.scale.category20c();
var bubble = d3.layout.pack()
.sort(null)
.size([diameter, diameter])
.value(function(d) { return (d.paramete5+1); })
.padding(1.5);
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.attr("id","svg")
.attr("class", "bubble");
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(rootData))
.filter(function (d) {
return !d.children;
}))
.enter().append("g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.append("title")
.text(function(d) { return d.className + ": " + format(d.value); });
svg.append("defs")
.append("pattern")
.attr("id", "image")
.attr("height","100%")
.attr("width","100%")
.attr("x", "37%")
.attr("y","40%")
.append("image")
.attr("x", "0%")
.attr("y","0%")
.attr("viewBox", "0 0 200 200")
.attr("height","100")
.attr("width","100")
.attr("xlink:href", "linkImage");
node.append("circle")
.attr("r", function (d) {
return d.r;
})
.style("opacity", "0")
.style("fill", function (d) {
return "black"
})
.attr("fill", 'url("#image")');
node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) { return d.className.substring(0, d.r / 3); })
.style("opacity", "0");
node.on("click", click);
function click(d) {
scope.$apply(function () {
console.log("Call on apply");
scope.myFunction(); // Call is just correct once scope.parameter1 = d.parameter1;
scope.paramete3 = d.paramete3;
scope.paramete2 = d.title;
scope.paramete4 = d.paramete4;
scope.paramete5 = d.paramete5;
});
d3.selectAll("circle").style("fill", function (d) {
return "black"
});
d3.select(this).select("circle").style('fill', 'circle');
d3.selectAll("circle").style("filter", null);
d3.select(this).select("circle").style("filter", "url(#f1)");
var test = document.getElementById("svg");
function whatClicked(evt) {
if (evt.target.id == "svg"){
d3.selectAll("circle").style("fill", function (d) {
return "black"
});
}
scope.myFunction();
}
test.addEventListener("click", whatClicked, true);
}
// Returns a flattened hierarchy containing all leaf nodes under the root.
function classes(root) {
var classes = [];
function recurse(name, node) {
if (node.children) node.children.forEach(function (child) {
recurse(node.name, child);
});
else classes.push({
parameter1: name,
className: node.name,
title: node.title,
value: node.paramete5,
paramete4: node.projectId,
parameter6: node.parameter7,
paramete5: node.paramete5,
paramete3: node.paramete3,
isLocked:node.isLocked
});
}
recurse(null, root);
return {children: classes};
}
d3.select(self.frameElement).style("height", diameter + "px");
}
if(typeof scope.chartData != "undefined"){
scope.drawChart(scope.chartData);
}
}
};
}]);
Here's the plunker: https://plnkr.co/edit/Kzr2a4HFG2yJxWfu90ri?p=preview
If you want to show an image in the background of a svg element use defs:
svg.append("defs")
.append("pattern")
.attr("id", "image")
.attr("height","100%")
.attr("width","100%")
.attr("x", "37%")
.attr("y","40%")
.append("image")
.attr("x", "0%")
.attr("y","0%")
.attr("viewBox", "0 0 200 200")
.attr("height","100")
.attr("width","100")
.attr("xlink:href", "ImageLink");
And give this reference to your element by url:
.attr("fill", 'url("#image")');
Just started learning meteor and d3 / crossfilter charting libraries.
Picked up some example code off the Web, and have it working in my local app.
I do have an empty this.autorun() function in my meteor client code, but have no idea what part of the lengthy d3 initialization and composition routine should be put into autorun, in order for these charts to react to the data changes.
I have tried to just put the Flights.find().fetch() inside the autorun, but in that case, the page never seem to finish loading.
Here is my entire meteor code:
if (Meteor.isClient) {
Template.dashboard.helpers({
});
Template.dashboard.events({
});
Template.dashboard.rendered = function(){
var flights = Flights.find().fetch();
if (!flights.length) return;
var crossData = crossfilter(flights);
// d3.csv(data, function(error, flights) {
// Various formatters.
var formatNumber = d3.format(",d"),
formatChange = d3.format("+,d"),
formatDate = d3.time.format("%B %d, %Y"),
formatTime = d3.time.format("%I:%M %p");
// A nest operator, for grouping the flight list.
var nestByDate = d3.nest()
.key(function(d) { return d3.time.day(d.date); });
// A little coercion, since the CSV is untyped.
flights.forEach(function(d, i) {
d.index = i;
d.date = parseDate(d.date);
d.delay = +d.delay;
d.distance = +d.distance;
});
// Create the crossfilter for the relevant dimensions and groups.
var flight = crossfilter(flights),
all = flight.groupAll(),
date = flight.dimension(function(d) { return d.date; }),
dates = date.group(d3.time.day),
hour = flight.dimension(function(d) { return d.date.getHours() + d.date.getMinutes() / 60; }),
hours = hour.group(Math.floor),
delay = flight.dimension(function(d) { return Math.max(-60, Math.min(149, d.delay)); }),
delays = delay.group(function(d) { return Math.floor(d / 10) * 10; }),
distance = flight.dimension(function(d) { return Math.min(1999, d.distance); }),
distances = distance.group(function(d) { return Math.floor(d / 50) * 50; });
var charts = [
barChart()
.dimension(hour)
.group(hours)
.x(d3.scale.linear()
.domain([0, 24])
.rangeRound([0, 10 * 24])),
barChart()
.dimension(delay)
.group(delays)
.x(d3.scale.linear()
.domain([-60, 150])
.rangeRound([0, 10 * 21])),
barChart()
.dimension(distance)
.group(distances)
.x(d3.scale.linear()
.domain([0, 2000])
.rangeRound([0, 10 * 40])),
barChart()
.dimension(date)
.group(dates)
.round(d3.time.day.round)
.x(d3.time.scale()
.domain([new Date(2001, 0, 1), new Date(2001, 3, 1)])
.rangeRound([0, 10 * 90]))
.filter([new Date(2001, 1, 1), new Date(2001, 2, 1)])
];
// Given our array of charts, which we assume are in the same order as the
// .chart elements in the DOM, bind the charts to the DOM and render them.
// We also listen to the chart's brush events to update the display.
var chart = d3.selectAll(".chart")
.data(charts)
.each(function(chart) { chart.on("brush", renderAll).on("brushend", renderAll); });
// Render the initial lists.
var list = d3.selectAll(".list")
.data([flightList]);
// Render the total.
d3.selectAll("#total")
.text(formatNumber(flight.size()));
renderAll();
// Renders the specified chart or list.
function render(method) {
d3.select(this).call(method);
}
// Whenever the brush moves, re-rendering everything.
function renderAll() {
chart.each(render);
list.each(render);
d3.select("#active").text(formatNumber(all.value()));
}
// Like d3.time.format, but faster.
function parseDate(d) {
return new Date(2001,
d.substring(0, 2) - 1,
d.substring(2, 4),
d.substring(4, 6),
d.substring(6, 8));
}
window.filter = function(filters) {
filters.forEach(function(d, i) { charts[i].filter(d); });
renderAll();
};
window.reset = function(i) {
charts[i].filter(null);
renderAll();
};
function flightList(div) {
var flightsByDate = nestByDate.entries(date.top(40));
div.each(function() {
var date = d3.select(this).selectAll(".date")
.data(flightsByDate, function(d) { return d.key; });
date.enter().append("div")
.attr("class", "date")
.append("div")
.attr("class", "day")
.text(function(d) { return formatDate(d.values[0].date); });
date.exit().remove();
var flight = date.order().selectAll(".flight")
.data(function(d) { return d.values; }, function(d) { return d.index; });
var flightEnter = flight.enter().append("div")
.attr("class", "flight");
flightEnter.append("div")
.attr("class", "time")
.text(function(d) { return formatTime(d.date); });
flightEnter.append("div")
.attr("class", "origin")
.text(function(d) { return d.origin; });
flightEnter.append("div")
.attr("class", "destination")
.text(function(d) { return d.destination; });
flightEnter.append("div")
.attr("class", "distance")
.text(function(d) { return formatNumber(d.distance) + " mi."; });
flightEnter.append("div")
.attr("class", "delay")
.classed("early", function(d) { return d.delay < 0; })
.text(function(d) { return formatChange(d.delay) + " min."; });
flight.exit().remove();
flight.order();
});
}
function barChart() {
if (!barChart.id) barChart.id = 0;
var margin = {top: 10, right: 10, bottom: 20, left: 10},
x,
y = d3.scale.linear().range([100, 0]),
id = barChart.id++,
axis = d3.svg.axis().orient("bottom"),
brush = d3.svg.brush(),
brushDirty,
dimension,
group,
round;
function chart(div) {
var width = x.range()[1],
height = y.range()[0];
y.domain([0, group.top(1)[0].value]);
div.each(function() {
var div = d3.select(this),
g = div.select("g");
// Create the skeletal chart.
if (g.empty()) {
div.select(".title").append("a")
.attr("href", "javascript:reset(" + id + ")")
.attr("class", "reset")
.text("reset")
.style("display", "none");
g = div.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
g.append("clipPath")
.attr("id", "clip-" + id)
.append("rect")
.attr("width", width)
.attr("height", height);
g.selectAll(".bar")
.data(["background", "foreground"])
.enter().append("path")
.attr("class", function(d) { return d + " bar"; })
.datum(group.all());
g.selectAll(".foreground.bar")
.attr("clip-path", "url(#clip-" + id + ")");
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(axis);
// Initialize the brush component with pretty resize handles.
var gBrush = g.append("g").attr("class", "brush").call(brush);
gBrush.selectAll("rect").attr("height", height);
gBrush.selectAll(".resize").append("path").attr("d", resizePath);
}
// Only redraw the brush if set externally.
if (brushDirty) {
brushDirty = false;
g.selectAll(".brush").call(brush);
div.select(".title a").style("display", brush.empty() ? "none" : null);
if (brush.empty()) {
g.selectAll("#clip-" + id + " rect")
.attr("x", 0)
.attr("width", width);
} else {
var extent = brush.extent();
g.selectAll("#clip-" + id + " rect")
.attr("x", x(extent[0]))
.attr("width", x(extent[1]) - x(extent[0]));
}
}
g.selectAll(".bar").attr("d", barPath);
});
function barPath(groups) {
var path = [],
i = -1,
n = groups.length,
d;
while (++i < n) {
d = groups[i];
path.push("M", x(d.key), ",", height, "V", y(d.value), "h9V", height);
}
return path.join("");
}
function resizePath(d) {
var e = +(d == "e"),
x = e ? 1 : -1,
y = height / 3;
return "M" + (.5 * x) + "," + y
+ "A6,6 0 0 " + e + " " + (6.5 * x) + "," + (y + 6)
+ "V" + (2 * y - 6)
+ "A6,6 0 0 " + e + " " + (.5 * x) + "," + (2 * y)
+ "Z"
+ "M" + (2.5 * x) + "," + (y + 8)
+ "V" + (2 * y - 8)
+ "M" + (4.5 * x) + "," + (y + 8)
+ "V" + (2 * y - 8);
}
}
brush.on("brushstart.chart", function() {
var div = d3.select(this.parentNode.parentNode.parentNode);
div.select(".title a").style("display", null);
});
brush.on("brush.chart", function() {
var g = d3.select(this.parentNode),
extent = brush.extent();
if (round) g.select(".brush")
.call(brush.extent(extent = extent.map(round)))
.selectAll(".resize")
.style("display", null);
g.select("#clip-" + id + " rect")
.attr("x", x(extent[0]))
.attr("width", x(extent[1]) - x(extent[0]));
dimension.filterRange(extent);
});
brush.on("brushend.chart", function() {
if (brush.empty()) {
var div = d3.select(this.parentNode.parentNode.parentNode);
div.select(".title a").style("display", "none");
div.select("#clip-" + id + " rect").attr("x", null).attr("width", "100%");
dimension.filterAll();
}
});
chart.margin = function(_) {
if (!arguments.length) return margin;
margin = _;
return chart;
};
chart.x = function(_) {
if (!arguments.length) return x;
x = _;
axis.scale(x);
brush.x(x);
return chart;
};
chart.y = function(_) {
if (!arguments.length) return y;
y = _;
return chart;
};
chart.dimension = function(_) {
if (!arguments.length) return dimension;
dimension = _;
return chart;
};
chart.filter = function(_) {
if (_) {
brush.extent(_);
dimension.filterRange(_);
} else {
brush.clear();
dimension.filterAll();
}
brushDirty = true;
return chart;
};
chart.group = function(_) {
if (!arguments.length) return group;
group = _;
return chart;
};
chart.round = function(_) {
if (!arguments.length) return round;
round = _;
return chart;
};
return d3.rebind(chart, brush, "on");
}
// });
this.autorun(function(){
})
}
}
if (Meteor.isServer) {
Meteor.startup(function () {
});
}
If this helps, here is my attempt at reproducing one of the d3 force layout examples with collision detection / custom gravity functions https://gist.github.com/gmlnchv/80dd206440cca39800b8. I'm using observe() to react to changes.
I have a public-facing site using GoogleMaps API v3 and the MarkerClusterer library. The page that is having a problem can be found here (http://www.mihomes.com/Find-Your-New-Home/San-Antonio-Homes). If you view this page in IE9 or Chrome, the correct pin icon images are shown, while in previous IE versions and Firefox different (and incorrect) pin icons.
IE9
Firefox 3.6.8
Here is the JavaScript code that generates the pins/clustering:
$(function() {
var dl_grid = new DLGrid(".GMapGrid");
dl_grid.init();
var dl_map = new DLMap("#map");
dl_map.init();
var markers = dl_grid.CollectMarkerData();
dl_map.LoadMarkers(markers);
});
var DLIcons = new function() {
var me = this;
me.NormalIcon = "/images/GoogleMapsIcons/mi_icon_n.png";
me.HoverIcon = "/images/GoogleMapsIcons/new_mi_icon_r.png";
me.ClusterIcon = "/images/GoogleMapsIcons/mi_icon_n.png";
me.ClusterHoverIcon = "/images/GoogleMapsIcons/new_mi_icon_r.png";
me.SalesCenterIcon = "/images/GoogleMapsIcons/new_mi_icon_n2.gif";
me.DesignCenterIcon = "/images/GoogleMapsIcons/mi_dc_n.png";
me.DesignCenterHoverIcon = "/images/GoogleMapsIcons/mi_dc_r.png";
};
//Used for all functions relating to the table below the map
var DLGrid = function(grid_selector) {
//Initialize variables
var me = this;
me.grid = $(grid_selector);
//Initialize
me.init = function() {
setupTableSorting();
setupHoverEvents();
setupZoomButtons();
};
//Setup the table sorting
var setupTableSorting = function() {
//Init tablesorter plugin
var sort_options = { headers: { 0: { sorter: false }, 4: { sorter: false} } };
if (MI_DIVISION_LANDING_TABLE_SORT_OPTIONS != undefined) {
sort_options = MI_DIVISION_LANDING_TABLE_SORT_OPTIONS;
}
me.grid.tablesorter(sort_options);
//As soon as the user sorts, remove all Community Groups
me.grid.bind("sortEnd", function() {
me.grid.find("tr.communityGroup").remove();
$("tr.groupedCommunity").removeClass("groupedCommunity").addClass("ungroupedCommunity");
//$("tr.ungroupedCommunity").removeClass("ungroupedCommunity");
me.grid.trigger("update");
});
};
var highlightRow = function(marker) {
var markerId = (typeof (marker) == "string") ? marker : marker.jsonData.MarkerID;
$(me.grid).find("#" + markerId).addClass("highlightedRow");
};
// Bind to mouseover/mouseout events and highlight the proper row in the table
// Trigger mouseover/mouseout events when you hover over a row in the table
var setupHoverEvents = function() {
$(document).bind("MARKER_MOUSEOVER", function(e, marker) {
$(me.grid).find("tbody tr.highlightedRow").removeClass("highlightedRow");
if (typeof (marker) != "string" && marker.length != undefined) {
for (var i = 0; i < marker.length; i++) {
highlightRow(marker[i]);
}
}
else {
highlightRow(marker);
}
});
// $(document).bind("MULTIPLE_MARKER_MOUSEOVER", function(e, markers) {
// $(me.grid).find("tbody tr.highlightedRow").removeClass("highlightedRow");
// for (var i = 0; i < markers.length; i++) {
// var markerId = (typeof (markers[i]) == "string") ? markers[i] : markers[i].jsonData.MarkerID;
// $(me.grid).find("#" + markerId).addClass("highlightedRow");
// }
// });
$(me.grid).find("tbody tr").mouseover(function() {
$(document).trigger("MARKER_MOUSEOVER", [$(this).attr("id")]);
});
};
// The zoom buttons next to each row should zoom to a marker and show it's info window
var setupZoomButtons = function() {
$(me.grid).find("tbody tr .zoom_link img").click(function() {
$(document).trigger("MAP_SHOW_MARKER_POPUP_AND_ZOOM", [$(this).parent().parent().attr("id")]);
});
};
// Collect and parse the JSON data from the hidden 'data' column in the table
me.CollectMarkerData = function() {
var markers = [];
$.each(me.grid.find("tbody tr:not(.communityGroup)"), function(i, row) {
var dataCell = $(row).children("td.data");
var rawContent = $(dataCell).text();
var json_data = {};
if (rawContent.length > 0) {
json_data = JSON.parse(rawContent);
}
json_data["MarkerID"] = $(row).attr("id");
markers.push(json_data);
});
return markers;
};
};
//Used for all functions relating to map
var DLMap = function(map_div_selector) {
//Initialize variables
var me = this;
me.mapDivSelector = map_div_selector;
me.mapObj;
me.init = function() {
setupMap();
bindHoverEvents();
setupPopupEvents();
setupDesignCenter();
};
//Basic setup of map
var setupMap = function() {
me.mapObj = new DLGoogleMap(me.mapDivSelector);
me.mapObj.init(onMarkerMouseOver, showMarkerPopup);
};
// Add an array of markers (from json data) to the map
me.LoadMarkers = function(markers) {
$.each(markers, function(i, json_marker) {
me.mapObj.addMarker(json_marker);
});
me.mapObj.fitMapToMarkers();
};
var showMarkerPopup = function(markerJson, zoomToLocation) {
var source = $("#MapMarkerInfoWindow").html();
var template = Handlebars.compile(source);
var content = template(markerJson);
var triggerKey = (zoomToLocation == true) ? "MAP_SHOW_POPUP_AND_ZOOM" : "MAP_SHOW_POPUP";
$(document).trigger(triggerKey, [content, markerJson.Lat, markerJson.Lng]);
};
var onMarkerMouseOver = function(markerJson) {
$(document).trigger("MARKER_MOUSEOVER", markerJson);
}
// Highlight (or unhighlight) a marker when a mouseover/mouseout event is triggered
var bindHoverEvents = function() {
$(document).bind("MARKER_MOUSEOVER", function(e, marker) {
if (typeof (marker) != "string" && marker.length != undefined) {
marker = marker[0];
}
me.mapObj.resetMarkerHighlighting();
me.mapObj.highlightMarker(marker);
});
// $(document).bind("MULTIPLE_MARKER_MOUSEOVER", function(e, markers) {
// me.mapObj.resetMarkerHighlighting();
// if (markers[0].cluster != null) {
// me.mapObj.highlightCluster(markers[0]
// }
// });
};
var setupPopupEvents = function() {
$(document).bind("MAP_SHOW_POPUP", function(e, content, lat, lng) {
me.mapObj.showPopup(content, lat, lng);
});
$(document).bind("MAP_SHOW_POPUP_AND_ZOOM", function(e, content, lat, lng) {
me.mapObj.showPopup(content, lat, lng, true);
});
$(document).bind("MAP_SHOW_MARKER_POPUP", function(e, marker) {
if (typeof (marker) == "string") {
marker = me.mapObj.findMarkerByID(marker);
}
showMarkerPopup(marker.jsonData);
});
$(document).bind("MAP_SHOW_MARKER_POPUP_AND_ZOOM", function(e, marker) {
if (typeof (marker) == "string") {
marker = me.mapObj.findMarkerByID(marker);
}
showMarkerPopup(marker.jsonData, true);
});
};
var setupDesignCenter = function() {
var jsonText = $.trim($("#DesignCenterData").text());
if (jsonText.length > 5) {
var dcJson = JSON.parse(jsonText);
me.mapObj.addDesignCenterMarker(dcJson);
}
};
};
var DLGoogleMap = function(map_div_selector) {
//Initialize variables
var me = this;
me.mapDiv = $(map_div_selector);
me.gmap;
me.markers = [];
me.markerClusterer;
me.infoWindow;
me.onMouseOver;
me.onClick;
me.ZOOM_TO_LEVEL = 14;
me.highlightedMarkers = [];
me.designCenterMarker = null;
//Extend Google Map Classes
google.maps.Marker.prototype.jsonData = null;
google.maps.Marker.prototype.iconImg = null;
google.maps.Marker.prototype.iconHoverImg = null;
me.init = function(onMouseOver, onClick) {
me.onMouseOver = onMouseOver;
me.onClick = onClick;
setupMap();
setupClustering();
setupDrivingDirectionLinks();
};
var setupMap = function() {
//var latlng = new google.maps.LatLng(40.05, -82.95);
var myOptions = {
zoom: 14,
scrollwheel: false,
mapTypeId: google.maps.MapTypeId.TERRAIN
};
me.gmap = new google.maps.Map(document.getElementById("map"), myOptions);
me.infoWindow = new google.maps.InfoWindow();
};
var setupDrivingDirectionLinks = function() {
$("a.gDirectionsLink").live("click", function(e) {
e.preventDefault();
$(".gPopupInfo").hide();
$(".gDrivingDirections").show();
});
$("a.gCloseDrivingDirections").live("click", function(e) {
e.preventDefault();
$(".gDrivingDirections").hide();
$(".gPopupInfo").show();
});
};
//Add a single json marker to the map
me.addMarker = function(jsonMarker) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(jsonMarker.Lat, jsonMarker.Lng),
title: jsonMarker.Name
});
marker.jsonData = jsonMarker;
if (jsonMarker.HasSalesCenter == "True") {
marker.iconImg = DLIcons.SalesCenterIcon;
}
else {
marker.iconImg = DLIcons.NormalIcon;
}
marker.iconHoverImg = DLIcons.HoverIcon;
marker.icon = marker.iconImg;
google.maps.event.addListener(marker, 'click', function() {
me.onClick(marker.jsonData);
});
google.maps.event.addListener(marker, 'mouseover', function() { me.onMouseOver(marker) });
me.markerClusterer.addMarker(marker);
me.markers.push(marker);
};
//Add an arbitrary marker
me.addDesignCenterMarker = function(dcJson) {
me.designCenterMarker = new google.maps.Marker({
position: new google.maps.LatLng(dcJson.Lat, dcJson.Lng),
title: "Design Center",
map: me.gmap
});
me.designCenterMarker.jsonData = dcJson;
me.designCenterMarker.iconImg = DLIcons.DesignCenterIcon;
me.designCenterMarker.iconHoverImg = DLIcons.DesignCenterHoverIcon;
me.designCenterMarker.icon = me.designCenterMarker.iconImg;
google.maps.event.addListener(me.designCenterMarker, 'mouseover', function() {
me.highlightMarker(me.designCenterMarker);
});
google.maps.event.addListener(me.designCenterMarker, 'mouseout', function() {
me.unHighlightMarker(me.designCenterMarker);
});
google.maps.event.addListener(me.designCenterMarker, 'click', function() {
me.infoWindow.close();
var source = $("#DesignCenterInfoWindow").html();
var template = Handlebars.compile(source);
var content = template(me.designCenterMarker.jsonData);
me.showPopup(content, me.designCenterMarker.jsonData.Lat, me.designCenterMarker.jsonData.Lng);
});
};
me.resetMarkerHighlighting = function() {
for (var i = 0; i < me.highlightedMarkers.length; i++) {
me.unHighlightMarker(me.highlightedMarkers[i]);
}
me.highlightedMarkers = [];
}
me.highlightMarker = function(m) {
var marker = (typeof (m) == "string") ? me.findMarkerByID(m) : m;
if (marker != null) {
if (marker.cluster == null || (marker.cluster.hasMultipleMarkers() == false)) {
marker.setIcon(marker.iconHoverImg);
}
else {
highlightCluster(marker.cluster);
}
me.highlightedMarkers.push(marker);
}
};
me.unHighlightMarker = function(m) {
var marker = (typeof (m) == "string") ? me.findMarkerByID(m) : m;
if (marker != null) {
if (marker.cluster == null || (marker.cluster.hasMultipleMarkers() == false)) {
marker.setIcon(marker.iconImg);
}
else {
unHighlightCluster(marker.cluster);
}
}
};
me.zoomAndShowPopup = function(content, lat, lng) {
};
me.showPopup = function(content, lat, lng, zoomToLocation) {
if (zoomToLocation == true) {
var adjustedLat = lat - (me.gmap.getZoom() * 0.01);
me.zoomToLocation(adjustedLat, lng);
}
me.infoWindow.setContent(content);
me.infoWindow.setPosition(new google.maps.LatLng(lat, lng));
me.infoWindow.open(me.gmap);
};
me.zoomToLocation = function(lat, lng) {
me.gmap.setZoom(me.ZOOM_TO_LEVEL)
me.gmap.setCenter(new google.maps.LatLng(lat, lng));
};
me.fitMapToMarkers = function() {
me.markerClusterer.fitMapToMarkers();
};
//Setup the map 'clustering', so that nearby markers will cluster together to form 1 icon
var setupClustering = function() {
Cluster.prototype.iconImg = DLIcons.ClusterIcon;
Cluster.prototype.iconHoverImg = DLIcons.ClusterHoverIcon;
var mc_options = {
gridSize: 15,
maxZoom: 15,
zoomOnClick: false,
styles: [{ url: DLIcons.ClusterIcon, height: 30, width: 20, textColor: "#fff", textSize: 10}],
showMarkerCount: false,
onClusterAdded: onClusterAdded
};
me.markerClusterer = new MarkerClusterer(me.gmap, [], mc_options);
//Setup Cluster Info Windows with a list of marker links
google.maps.event.addListener(me.markerClusterer, 'clusterclick', function(cluster) {
me.infoWindow.close();
var source = $("#MapClusterInfoWindow").html();
var template = Handlebars.compile(source);
var markers = getClusterJsonMarkers(cluster);
var data = {
markers: markers
};
var content = template(data);
$(document).trigger("MAP_SHOW_POPUP", [content, cluster.getCenter().lat(), cluster.getCenter().lng()]);
});
//Setup Cluster marker highlighting
google.maps.event.addListener(me.markerClusterer, 'clustermouseover', function(cluster) {
me.resetMarkerHighlighting();
highlightCluster(cluster);
var cmarkers = cluster.getMarkers();
$(document).trigger("MARKER_MOUSEOVER", [cmarkers]);
});
$(".openMarkerPopup").live("click", function(e) {
e.preventDefault();
$(document).trigger("MAP_SHOW_MARKER_POPUP", [$(this).attr("rel")]);
});
};
var onClusterAdded = function(cluster) {
if (clusterHasSalesCenter(cluster)) {
cluster.iconImg = DLIcons.SalesCenterIcon;
cluster.updateIconUrl(cluster.iconImg);
}
else {
cluster.iconImg = DLIcons.ClusterIcon;
}
};
var clusterHasSalesCenter = function(cluster) {
if ((cluster == undefined) || (cluster.markers_ == undefined)) {
return false;
}
for (var i = 0; i < cluster.markers_.length; i++) {
if (cluster.markers_[i].jsonData.HasSalesCenter == "True") {
return true;
}
}
return false;
};
var highlightCluster = function(cluster) {
//me.highlightedMarkers = [];
// for (var i = 0; i < cluster.markers_.length; i++) {
// me.highlightedMarkers.push(cluster.markers_[i]);
// }
cluster.updateIconUrl(cluster.iconHoverImg);
};
var unHighlightCluster = function(cluster) {
cluster.updateIconUrl(cluster.iconImg);
};
var getClusterJsonMarkers = function(cluster) {
var jsonMarkers = [];
var gmarkers = cluster.getMarkers();
for (var i = 0; i < gmarkers.length; i++) {
jsonMarkers.push(gmarkers[i].jsonData);
}
return jsonMarkers;
};
// Get a marker on the map given it's MarkerID
me.findMarkerByID = function(markerId) {
for (var i = 0; i < me.markers.length; i++) {
if (me.markers[i].jsonData.MarkerID == markerId) {
return me.markers[i];
break;
}
}
return null;
};
};
This website is built using .NET Framework 3.5 and ASP.NET WebForms.
Thanks for the assistance.