How to stack Charts with amchart? - graph

is it possible to stack 2 or more graphs in one chart? with the same Scrollbar / Chartcursor?
I tried it with the live editor, but havn't found any option to make this possible.
Here som pictures for explaination:
At the moment i have this graph: http://imgur.com/XpU42xK
But i want a graph like this: http://imgur.com/xdwJ7lu
Thanks for your help :)

Just for the sake of sport I tried to implement it. Good news - it's possible :)
First of all you will need to prepare both charts
First chart
Scrollbar enabled
Chart cursor enabled
Category axis hidden
Legend disabled
Bottom margin: 0
Second chart
Scrollbar disabled
Chart cursor enabled
Category axis enabled
Legend enabled
Top margin: 0
This creates two charts, "glued" together like in your mockup:
Syncing the charts
They still act on their own, like two separate charts. We have to sync them up:
Cursor
Legend
Zoom/pan
That's where this code comes in:
/**
* Use events to sync up cursors and zooms
*/
for ( var x = 0; x < AmCharts.charts.length; x++ ) {
// use "changed" event to track cursor movement on all charts
// place the cursor on other charts on the same index
AmCharts.charts[ x ].addListener( "changed", function( event ) {
// find which category is currently being rolled over
var category = event.chart.dataProvider[ event.index ][ event.chart.categoryField ];
// cycle through all available charts and place the cursor at
// the same category
for ( var i = 0; i < AmCharts.charts.length; i++ ) {
if ( event.chart !== AmCharts.charts[ i ] ) {
AmCharts.charts[ i ].chartCursor.showCursorAt( category );
}
}
} );
// use "zoomed" event to track zooms/pans so we can apply
// the same zoom across other charts
AmCharts.charts[ x ].addListener( "zoomed", function( zoomEvent ) {
// cycle through all available charts and apply same zoom
for ( var i = 0; i < AmCharts.charts.length; i++ ) {
if ( zoomEvent.chart !== AmCharts.charts[ i ] && ( AmCharts.charts[ i ].startIndex != zoomEvent.startIndex || AmCharts.charts[ i ].endIndex != zoomEvent.endIndex ) ) {
AmCharts.charts[ i ].zoomToIndexes( zoomEvent.startIndex, zoomEvent.endIndex );
}
}
} );
}
/**
* Use the last chart to display legend
* We will collect graphs from other charts and mirror them on
* the last chart.
*/
var lastChart = AmCharts.charts[ AmCharts.charts.length - 1 ];
for ( var x = 0; x < ( AmCharts.charts.length - 1 ); x++ ) {
// add graphs
for ( var g = 0; g < AmCharts.charts[ x ].graphs.length; g++ ) {
// create the related graph
var graph = new AmCharts.AmGraph();
graph.lineAlpha = 0;
graph.lineColor = AmCharts.charts[ x ].graphs[ g ].lineColor;
graph.relatedGraph = AmCharts.charts[ x ].graphs[ g ];
graph.valueField = "value_" + x + "_" + g;
graph.showBalloon = false;
graph.includeInMinMax = false;
graph.title = AmCharts.charts[ x ].graphs[ g ].title;
lastChart.addGraph( graph );
// mirror data for rollovers
for ( var i = 0; i < lastChart.dataProvider.length; i++ ) {
lastChart.dataProvider[ i ][ graph.valueField ] = AmCharts.charts[ x ].dataProvider[ i ][ AmCharts.charts[ x ].graphs[ g ].valueField ];
}
}
}
/**
* Add events to the legend of last chart so we can toggle
* graphs of the other charts
*/
lastChart.addListener( "init", function() {
// hide graph
lastChart.legend.addListener( "hideItem", function( event ) {
if ( event.dataItem.relatedGraph !== undefined ) {
event.dataItem.relatedGraph.chart.hideGraph( event.dataItem.relatedGraph );
}
} );
// show graph
lastChart.legend.addListener( "showItem", function( event ) {
if ( event.dataItem.relatedGraph !== undefined ) {
event.dataItem.relatedGraph.chart.showGraph( event.dataItem.relatedGraph );
}
} );
} );
And voila:
Here's a working demo:
/**
* First chart
* With scrollbar
* Without category axis
*/
AmCharts.makeChart( "chartdiv1", {
"type": "serial",
"theme": "light",
"path": "http://www.amcharts.com/lib/3/",
"dataProvider": [ {
"category": "Category #1",
"value": 2025
}, {
"category": "Category #2",
"value": 1882
}, {
"category": "Category #3",
"value": 1809
}, {
"category": "Category #4",
"value": 1322
}, {
"category": "Category #5",
"value": 1122
}, {
"category": "Category #6",
"value": 1114
}, {
"category": "Category #7",
"value": 984
}, {
"category": "Category #8",
"value": 711
}, {
"category": "Category #9",
"value": 665
}, {
"category": "Category #10",
"value": 580
} ],
"valueAxes": [ {
"gridAlpha": 0.2,
"dashLength": 0,
"showFirstLabel": false,
"ignoreAxisWidth": true,
"title": "First graph"
} ],
"startDuration": 1,
"graphs": [ {
"id": "g1",
"lineThickness": 2,
"lineColor": "#f56400",
"type": "step",
"valueField": "value",
"title": "Graph 1"
} ],
"chartCursor": {
"categoryBalloonEnabled": false,
"cursorColor": "#c30000",
"animationDuration": 0
},
"chartScrollbar": {},
"categoryField": "category",
"categoryAxis": {
"ignoreAxisWidth": true,
"labelsEnabled": false,
"axisAlpha": 0
},
"marginBottom": 0,
"marginLeft": 80
} );
/**
* Second chart
* Without scrollbar
* With category axis
*/
AmCharts.makeChart( "chartdiv2", {
"type": "serial",
"theme": "light",
"zoomOutText": "",
"path": "http://www.amcharts.com/lib/3/",
"dataProvider": [ {
"category": "Category #1",
"value": 521
}, {
"category": "Category #2",
"value": 215
}, {
"category": "Category #3",
"value": 655
}, {
"category": "Category #4",
"value": 601
}, {
"category": "Category #5",
"value": 122
}, {
"category": "Category #6",
"value": 114
}, {
"category": "Category #7",
"value": 521
}, {
"category": "Category #8",
"value": 351
}, {
"category": "Category #9",
"value": 222
}, {
"category": "Category #10",
"value": 156
} ],
"valueAxes": [ {
"gridAlpha": 0.2,
"dashLength": 0,
"showLastLabel": false,
"ignoreAxisWidth": true,
"title": "Second graph"
} ],
"startDuration": 1,
"graphs": [ {
"id": "g1",
"lineThickness": 2,
"lineColor": "#fae879",
"type": "step",
"valueField": "value",
"title": "Graph 2"
} ],
"chartCursor": {
"cursorColor": "#c30000",
"animationDuration": 0
},
"categoryField": "category",
"categoryAxis": {
"tickPosition": "middle"
},
"legend": {},
"marginTop": 0,
"marginLeft": 80
} );
/**
* Use events to sync up cursors and zooms
*/
for ( var x = 0; x < AmCharts.charts.length; x++ ) {
// use "changed" event to track cursor movement on all charts
// place the cursor on other charts on the same index
AmCharts.charts[ x ].addListener( "changed", function( event ) {
// find which category is currently being rolled over
var category = event.chart.dataProvider[ event.index ][ event.chart.categoryField ];
// cycle through all available charts and place the cursor at
// the same category
for ( var i = 0; i < AmCharts.charts.length; i++ ) {
if ( event.chart !== AmCharts.charts[ i ] ) {
AmCharts.charts[ i ].chartCursor.showCursorAt( category );
}
}
} );
// use "zoomed" event to track zooms/pans so we can apply
// the same zoom across other charts
AmCharts.charts[ x ].addListener( "zoomed", function( zoomEvent ) {
// cycle through all available charts and apply same zoom
for ( var i = 0; i < AmCharts.charts.length; i++ ) {
if ( zoomEvent.chart !== AmCharts.charts[ i ] && ( AmCharts.charts[ i ].startIndex != zoomEvent.startIndex || AmCharts.charts[ i ].endIndex != zoomEvent.endIndex ) ) {
AmCharts.charts[ i ].zoomToIndexes( zoomEvent.startIndex, zoomEvent.endIndex );
}
}
} );
}
/**
* Use the last chart to display legend
* We will collect graphs from other charts and mirror them on
* the last chart.
*/
var lastChart = AmCharts.charts[ AmCharts.charts.length - 1 ];
for ( var x = 0; x < ( AmCharts.charts.length - 1 ); x++ ) {
// add graphs
for ( var g = 0; g < AmCharts.charts[ x ].graphs.length; g++ ) {
// create the related graph
var graph = new AmCharts.AmGraph();
graph.lineAlpha = 0;
graph.lineColor = AmCharts.charts[ x ].graphs[ g ].lineColor;
graph.relatedGraph = AmCharts.charts[ x ].graphs[ g ];
graph.valueField = "value_" + x + "_" + g;
graph.showBalloon = false;
graph.includeInMinMax = false;
graph.title = AmCharts.charts[ x ].graphs[ g ].title;
lastChart.addGraph( graph );
// mirror data for rollovers
for ( var i = 0; i < lastChart.dataProvider.length; i++ ) {
lastChart.dataProvider[ i ][ graph.valueField ] = AmCharts.charts[ x ].dataProvider[ i ][ AmCharts.charts[ x ].graphs[ g ].valueField ];
}
}
}
/**
* Add events to the legend of last chart so we can toggle
* graphs of the other charts
*/
lastChart.addListener( "init", function() {
// hide graph
lastChart.legend.addListener( "hideItem", function( event ) {
if ( event.dataItem.relatedGraph !== undefined ) {
event.dataItem.relatedGraph.chart.hideGraph( event.dataItem.relatedGraph );
}
} );
// show graph
lastChart.legend.addListener( "showItem", function( event ) {
if ( event.dataItem.relatedGraph !== undefined ) {
event.dataItem.relatedGraph.chart.showGraph( event.dataItem.relatedGraph );
}
} );
} );
.chartdiv {
width: 100%;
height: 300px;
font-size: 11px;
}
<script src="http://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="http://www.amcharts.com/lib/3/serial.js"></script>
<script src="http://www.amcharts.com/lib/3/themes/light.js"></script>
<div id="chartdiv1" class="chartdiv"></div>
<div id="chartdiv2" class="chartdiv"></div>

Related

Why can't I use get_children_by_id() on this widget

I want to access the internal widgets but it gives me an error, that I can't index a nil value.
My widget:
local previewWidget = wibox()
previewWidget:setup {
widget = wibox.widget {
{
widget = wibox.widget {
{
widget = awful.widget.clienticon,
client = "",
id = "clientIcon"
},
{
widget = wibox.widget.textbox,
id = "titleText"
},
nil
},
layout = wibox.layout.align.horizontal,
id = "titleBox"
},
{
widget = wibox.widget.imagebox,
id = "clientImage"
},
nil
},
id = "previewBox",
layout = wibox.layout.align.vertical,
border_width = 10,
border_color = "FF0000",
}
This command doesn't give error:
local previewBox = previewWidget:get_children_by_id("previewBox")[1]
But this does:
local titleBox = previewWidget:get_children_by_id("titleBox")[1]
local clientIcon = previewWidget:get_children_by_id("clientIcon")[1]
What am I doing wrong here?
local previewWidget = wibox.widget {
{
{
widget = awful.widget.clienticon,
client = "",
id = "clientIcon"
},
{
widget = wibox.widget.textbox,
id = "titleText"
},
nil,
layout = wibox.layout.align.horizontal,
id = "titleBox"
},
{
widget = wibox.widget.imagebox,
id = "clientImage"
},
nil,
layout = wibox.layout.align.vertical,
id = "previewBox",
}
I incorrectly used the layout:
All 3 widgets needs to be where the layout is called
local some_widget = wibox.widget {
{ widget nr.1 },
{ widget nr.2 },
{ widget nr.3 },
layout = wibox.layout.align.vertical
}
P.S. it's enough to declare a layout without a widget

subcategories for each bar in multibar chart using chartjs

simple multi barchart only shows the main category ['one', 'two', 'three', 'four', 'five', 'six'] as the x axis labels. Is there a way to show subcategory ['A', 'B', 'C', 'D'] as secondary x axis labels in chartjs?
the graph with sub category labels
You can use grouped bar chart, like here:
const datasets = [{
label: "red",
backgroundColor: "red",
data: [33, 91, null, 48]
}, {
label: "blue",
backgroundColor: "blue",
data: [38, 57, 75, 84]
},
{
label: "yellow",
backgroundColor: "yellow",
data: [97, null, 67, 41]
}
];
new Chart(document.getElementById("myChart"), {
type: 'bar',
data: {
labels: ["one", "two", "three", "four"],
datasets
},
options: {
title: {
display: true,
text: 'Grouped bar chart'
},
scales: {
xAxes: [{
ticks: {
callback: function(label, index, labels, chart) {
let result = "" // initialize
datasets.forEach((dataset) => {
if (dataset.data[index] !== null) {
result += (result.length > 0 ? ', ' : '') + dataset.label;
}
})
return result
}
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="myChart" height="200"></canvas>
Here you can find more options and explanation: https://tobiasahlin.com/blog/chartjs-charts-to-get-you-started/

R plot_ly row or dataframe in PowerBI custom visuals

I am following: http://radacad.com/create-custom-visual-with-r-and-json-part3
Here it is mentioned that with below R code, we can plot the chart:
Values<-data.frame(X,Y,Legend,Row,Column)
g=ggplot(Values, aes(x=X, y=Y,colour = Legend))
I am using similar implementations.
Below code renders blank chart with only axis:
Values <- data.frame(mpg_test, cyl_test)
p <- plot_ly(Values,
x = ~mpg_test,
y = ~cyl_test,
name = "SF Zoo",
type = "bar"
)
Empty chart
Below code also renders empty chart:
Values <- data.frame(mpg_test, cyl_test)
p <- plot_ly(Values,
x = mpg_test,
y = cyl_test,
name = "SF Zoo",
type = "bar"
)
Below code renders correct chart:
Values <- data.frame(mpg_test, cyl_test)
p <- plot_ly(Values,
x = ~mpg_test[1:nrow(Values), ],
y = ~cyl_test[1:nrow(Values), ],
name = "SF Zoo",
type = "bar"
)
Correct chart:
My doubt is, as per the tutorial I must get columns in mpg_test and cyl_test, then why I am getting something like dataframe in here.
Does, plotly handles things differently with the usage of ~ ?
Please help. Also, is there any way I can use the mpg_test and cyl_test without nrow(Values) inside plot_ly
script.r
source('./r_files/flatten_HTML.r')
############### Library Declarations ###############
libraryRequireInstall("ggplot2");
libraryRequireInstall("plotly")
####################################################
################### Actual code ####################
Values <- data.frame(mpt_test, cyl_test)
p <- plot_ly(Values,
x = ~mpt_test[1:nrow(Values), ],
y = ~cyl_test[1:nrow(Values), ],
name = "SF Zoo",
type = "bar"
)
####################################################
############# Create and save widget ###############
#p = ggplotly(p);
internalSaveWidget(p, 'out.html');
####################################################
capabilities.json
{
"dataRoles": [
{
"displayName": "Mileage Per Gallon",
"kind": "GroupingOrMeasure",
"name": "mpg_test"
},
{
"displayName": "Cylinder Size",
"kind": "GroupingOrMeasure",
"name": "cyl_test"
}
],
"dataViewMappings": [
{ "conditions": [
{
"mpg_test": {
"max": 1
},
"cyl_test": {
"max": 1
}
}
],
"scriptResult": {
"dataInput": {
"table": {
"rows": {
"select": [
{
"for": {
"in": "mpg_test"
}
},
{
"for": {
"in": "cyl_test"
}
}
],
"dataReductionAlgorithm": {
"top": {}
}
}
}
},
"script": {
"scriptProviderDefault": "R",
"scriptOutputType": "html",
"source": {
"objectName": "rcv_script",
"propertyName": "source"
},
"provider": {
"objectName": "rcv_script",
"propertyName": "provider"
}
}
}
}
],
"objects": {
"rcv_script": {
"properties": {
"provider": {
"type": {
"text": true
}
},
"source": {
"type": {
"scripting": {
"source": true
}
}
}
}
}
},
"suppressDefaultTitle": true
}

Node click event not firing for D3 Force Directed Graph

I'm trying to make nodes collapsed on click of node itself. The graph at present is force directed graph with draggable nodes.
Problem I'm facing is, after attaching the node click event I cannot get the click to fire instead node is displaced from it's position (like dragged on mouse click).
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
var graph = { "links" : [ { "relation" : "Relation 0",
"source" : 0,
"target" : 1
},
{ "relation" : "Relation 1",
"source" : 1,
"target" : 3
},
{ "relation" : "Relation 3",
"source" : 1,
"target" : 4
},
{ "relation" : "Relation 4",
"source" : 1,
"target" : 5
},
{ "relation" : "Relation 5",
"source" : 0,
"target" : 2
},
{ "relation" : "Relation 6",
"source" : 2,
"target" : 6
}
],
"nodes" : [ { "collapsed" : false,
"collapsing" : 0,
"id" : 0,
"name" : "Node 0"
},
{ "collapsed" : false,
"collapsing" : 0,
"id" : 1,
"name" : "Node 1"
},
{ "collapsed" : false,
"collapsing" : 0,
"id" : 2,
"name" : "Node 2"
},
{ "collapsed" : false,
"collapsing" : 0,
"id" : 3,
"name" : "Node 3"
},
{ "collapsed" : false,
"collapsing" : 0,
"id" : 4,
"name" : "Node 4"
},
{ "collapsed" : false,
"collapsing" : 0,
"id" : 5,
"name" : "Node 5"
},
{ "collapsed" : false,
"collapsing" : 0,
"id" : 6,
"name" : "Node 6"
}
]
};
var edges = [];
graph.links.forEach(function(e) {
var sourceNode = graph.nodes.filter(function(n) {
return n.id === e.source;
})[0],
targetNode = graph.nodes.filter(function(n) {
return n.id === e.target;
})[0];
edges.push({
source: sourceNode,
target: targetNode,
relation: e.relation
});
});
update();
function update() {
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 8)
.attr("fill", function(d) { return color(d.group); })
.on("click", togglenode)
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("title")
.text(function(d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
}
function dragstarted(d) {
console.log('drag started');
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
console.log('dragged');
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
console.log('drag ended');
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
function togglenode(d) {
console.log('clicked node : '+d.id);
if (!d3.event.defaultPrevented) {
var inc = d.collapsed ? -1 : 1;
recurse(d);
function recurse(sourceNode){
//check if link is from this node, and if so, collapse
edges.forEach(function(l) {
if (l.source.id === sourceNode.id){
l.target.collapsing += inc;
recurse(l.target);
}
});
}
d.collapsed = !d.collapsed;
update();
}
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 4.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.js"></script>
<svg width="800" height="500"></svg>

Animating Symbol on Polyline with GeoJson Using Google Map Api v.3

i have geojson like this :
{
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "Name": "Saluran I", "descriptio": null, "timestamp": null, "begin": null, "end": null, "altitudeMo": null, "tessellate": 1, "extrude": -1, "visibility": -1, "drawOrder": null, "icon": null }, "geometry": { "type": "LineString", "coordinates": [ [ 115.16149264388849, -8.692345779607091 ], [ 115.1619799975747, -8.693099022961922 ], [ 115.1621209969948, -8.693450580043523 ], [ 115.1621893956145, -8.69367970180001 ], [ 115.1622369286341, -8.693885376054253 ], [ 115.16248624315379, -8.694125330077087 ], [ 115.1625293838875, -8.694297689361708 ], [ 115.1625662929434, -8.694480291402353 ], [ 115.1638244075253, -8.694489484612449 ] ] } },
{ "type": "Feature", "properties": { "Name": "Saluran II", "descriptio": null, "timestamp": null, "begin": null, "end": null, "altitudeMo": null, "tessellate": 1, "extrude": -1, "visibility": -1, "drawOrder": null, "icon": null }, "geometry": { "type": "LineString", "coordinates": [ [ 115.1647420393289, -8.691263798416188 ], [ 115.16480885306601, -8.691910749059817 ], [ 115.1648021389716, -8.692020041570267 ], [ 115.16476460026691, -8.692197857370241 ], [ 115.16467300953239, -8.692311184386924 ], [ 115.1645832229062, -8.692570845169653 ], [ 115.164590403574, -8.69280000074686 ], [ 115.1642184661912, -8.692878192437396 ], [ 115.16418831658579, -8.693471791565786 ], [ 115.16411080877791, -8.69417746825723 ], [ 115.1639500244154, -8.694469415766308 ], [ 115.1638498474275, -8.69450385891758 ], [ 115.1637726241196, -8.6949183073729 ], [ 115.1637482310352, -8.695106949243888 ], [ 115.16369576938609, -8.695516868583109 ], [ 115.1633930487843, -8.695552277605064 ], [ 115.1628619559151, -8.695616245099258 ], [ 115.1628453449146, -8.695861398016726 ], [ 115.1625531407406, -8.695981675836846 ], [ 115.1619167160671, -8.696110249672243 ], [ 115.1621001879372, -8.697348692504496 ], [ 115.1619454016469, -8.697429501488445 ] ] } },
{ "type": "Feature", "properties": { "Name": "Saluran III", "descriptio": null, "timestamp": null, "begin": null, "end": null, "altitudeMo": null, "tessellate": 1, "extrude": -1, "visibility": -1, "drawOrder": null, "icon": null }, "geometry": { "type": "LineString", "coordinates": [ [ 115.1649918428157, -8.698726927918452 ], [ 115.1647717936939, -8.698771893626452 ], [ 115.16464767674699, -8.698766178177538 ], [ 115.164531974608, -8.698766747854149 ], [ 115.1643076171094, -8.698759607942607 ], [ 115.1639182931609, -8.698808262913275 ], [ 115.1637251462856, -8.69887278452787 ], [ 115.1633890383243, -8.698973728970067 ], [ 115.1626822904817, -8.699201749498535 ], [ 115.1626359629325, -8.699082282748639 ], [ 115.1621957225977, -8.698410881596306 ], [ 115.16193358395429, -8.69743434218265 ] ] } }
]
}
i can showing that geojson on my map,
line.loadGeoJson(urlBase + 'peta/aliran.geojson');
// styling features aliran
line.setStyle(function(feature){
return {
strokeColor : '#0000FF',
strokeWeight : 5,
zIndex : 1
}
});
// set map;
line.setMap(map);
but i have problem when i want to animating symbols on the line like this : https://developers.google.com/maps/documentation/javascript/examples/overlay-symbol-animate. could anybody help me ?
Leveraging the code in this related example (which animates a marker along a polyline from the directions service) to animate a symbol along one of the polylines in your data:
proof of concept fiddle
code snippet:
var map;
var position;
var marker = null;
var polyline = null;
var poly2 = null;
var speed = 0.000005,
wait = 1;
var infowindow = null;
var myPano;
var panoClient;
var nextPanoId;
var timerHandle = null;
var polyline;
function initialize() {
map = new google.maps.Map(
document.getElementById("map_canvas"), {
center: new google.maps.LatLng(37.4419, -122.1419),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var bounds = new google.maps.LatLngBounds();
map.data.addListener('addfeature', function(e) {
processPoints(e.feature.getGeometry(), bounds.extend, bounds);
if (e.feature.getGeometry().getType() == "LineString") {
polyline = new google.maps.Polyline({
map: map,
path: e.feature.getGeometry().getArray(),
strokeWeight: 5,
strokeOpacity: 0.4,
strokeColor: 'red'
});
marker = createMarker(polyline.getPath().getAt(0), "here", "marker text");
setTimeout(startAnimation, 2000);
}
map.fitBounds(bounds);
});
// var line = google.maps.Data;
map.data.addGeoJson(geoJson);
// styling features aliran
map.data.setStyle(function(feature) {
return {
strokeColor: '#0000FF',
strokeWeight: 1,
zIndex: 1
}
});
// set map;
map.data.setMap(map);
}
var step = 5; // metres
var tick = 100; // milliseconds
var eol;
var k = 0;
var stepnum = 0;
var speed = "";
var lastVertex = 1;
//=============== animation functions ======================
function updatePoly(d) {
// Spawn a new polyline every 20 vertices, because updating a 100-vertex poly is too slow
if (poly2.getPath().getLength() > 20) {
poly2 = new google.maps.Polyline([polyline.getPath().getAt(lastVertex - 1)]);
}
if (polyline.GetIndexAtDistance(d) < lastVertex + 2) {
if (poly2.getPath().getLength() > 1) {
poly2.getPath().removeAt(poly2.getPath().getLength() - 1)
}
poly2.getPath().insertAt(poly2.getPath().getLength(), polyline.GetPointAtDistance(d));
} else {
poly2.getPath().insertAt(poly2.getPath().getLength(), polyline.getPath().getAt(polyline.getPath().getLength() - 1));
}
}
function animate(d) {
if (d > eol) {
map.panTo(endLocation.latlng);
marker.setPosition(endLocation.latlng);
return;
}
var p = polyline.GetPointAtDistance(d);
map.panTo(p);
marker.setPosition(p);
updatePoly(d);
timerHandle = setTimeout("animate(" + (d + step) + ")", tick);
}
function startAnimation() {
if (timerHandle) clearTimeout(timerHandle);
eol = google.maps.geometry.spherical.computeLength(polyline.getPath());
map.setCenter(polyline.getPath().getAt(0));
poly2 = new google.maps.Polyline({
path: [polyline.getPath().getAt(0)],
strokeColor: "#0000FF",
strokeWeight: 10
});
setTimeout("animate(0)", tick); // Allow time for the initial map display
}
function processPoints(geometry, callback, thisArg) {
if (geometry instanceof google.maps.LatLng) {
callback.call(thisArg, geometry);
} else if (geometry instanceof google.maps.Data.Point) {
callback.call(thisArg, geometry.get());
} else {
geometry.getArray().forEach(function(g) {
processPoints(g, callback, thisArg);
});
}
}
// ==================================================
function createMarker(latlng, label, html) {
var contentString = '<b>' + label + '</b><br>' + html;
var marker = new google.maps.Marker({
position: latlng,
map: map,
title: label,
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: 8,
strokeColor: '#393'
},
zIndex: Math.round(latlng.lat() * -100000) << 5
});
marker.myname = label;
// gmarkers.push(marker);
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(contentString);
infowindow.open(map, marker);
});
return marker;
}
google.maps.event.addDomListener(window, "load", initialize);
var geoJson = {
"type": "FeatureCollection",
"crs": {
"type": "name",
"properties": {
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
"features": [{
"type": "Feature",
"properties": {
"Name": "Saluran II",
"descriptio": null,
"timestamp": null,
"begin": null,
"end": null,
"altitudeMo": null,
"tessellate": 1,
"extrude": -1,
"visibility": -1,
"drawOrder": null,
"icon": null
},
"geometry": {
"type": "LineString",
"coordinates": [
[115.1647420393289, -8.691263798416188],
[115.16480885306601, -8.691910749059817],
[115.1648021389716, -8.692020041570267],
[115.16476460026691, -8.692197857370241],
[115.16467300953239, -8.692311184386924],
[115.1645832229062, -8.692570845169653],
[115.164590403574, -8.69280000074686],
[115.1642184661912, -8.692878192437396],
[115.16418831658579, -8.693471791565786],
[115.16411080877791, -8.69417746825723],
[115.1639500244154, -8.694469415766308],
[115.1638498474275, -8.69450385891758],
[115.1637726241196, -8.6949183073729],
[115.1637482310352, -8.695106949243888],
[115.16369576938609, -8.695516868583109],
[115.1633930487843, -8.695552277605064],
[115.1628619559151, -8.695616245099258],
[115.1628453449146, -8.695861398016726],
[115.1625531407406, -8.695981675836846],
[115.1619167160671, -8.696110249672243],
[115.1621001879372, -8.697348692504496],
[115.1619454016469, -8.697429501488445]
]
}
}]
};
/*********************************************************************\
* *
* epolys.js by Mike Williams *
* updated to API v3 by Larry Ross *
* *
* A Google Maps API Extension *
* *
* Adds various Methods to google.maps.Polygon and google.maps.Polyline *
* *
* .Contains(latlng) returns true is the poly contains the specified *
* GLatLng *
* *
* .Area() returns the approximate area of a poly that is *
* not self-intersecting *
* *
* .Distance() returns the length of the poly path *
* *
* .Bounds() returns a GLatLngBounds that bounds the poly *
* *
* .GetPointAtDistance() returns a GLatLng at the specified distance *
* along the path. *
* The distance is specified in metres *
* Reurns null if the path is shorter than that *
* *
* .GetPointsAtDistance() returns an array of GLatLngs at the *
* specified interval along the path. *
* The distance is specified in metres *
* *
* .GetIndexAtDistance() returns the vertex number at the specified *
* distance along the path. *
* The distance is specified in metres *
* Returns null if the path is shorter than that *
* *
* .Bearing(v1?,v2?) returns the bearing between two vertices *
* if v1 is null, returns bearing from first to last *
* if v2 is null, returns bearing from v1 to next *
* *
* *
***********************************************************************
* *
* This Javascript is provided by Mike Williams *
* Blackpool Community Church Javascript Team *
* http://www.blackpoolchurch.org/ *
* http://econym.org.uk/gmap/ *
* *
* This work is licenced under a Creative Commons Licence *
* http://creativecommons.org/licenses/by/2.0/uk/ *
* *
***********************************************************************
* *
* Version 1.1 6-Jun-2007 *
* Version 1.2 1-Jul-2007 - fix: Bounds was omitting vertex zero *
* add: Bearing *
* Version 1.3 28-Nov-2008 add: GetPointsAtDistance() *
* Version 1.4 12-Jan-2009 fix: GetPointsAtDistance() *
* Version 3.0 11-Aug-2010 update to v3 *
* *
\*********************************************************************/
// === A method which returns a GLatLng of a point a given distance along the path ===
// === Returns null if the path is shorter than the specified distance ===
google.maps.Polyline.prototype.GetPointAtDistance = function(metres) {
// some awkward special cases
if (metres == 0) return this.getPath().getAt(0);
if (metres < 0) return null;
if (this.getPath().getLength() < 2) return null;
var dist = 0;
var olddist = 0;
for (var i = 1;
(i < this.getPath().getLength() && dist < metres); i++) {
olddist = dist;
dist += google.maps.geometry.spherical.computeDistanceBetween(this.getPath().getAt(i), this.getPath().getAt(i - 1));
}
if (dist < metres) {
return null;
}
var p1 = this.getPath().getAt(i - 2);
var p2 = this.getPath().getAt(i - 1);
var m = (metres - olddist) / (dist - olddist);
return new google.maps.LatLng(p1.lat() + (p2.lat() - p1.lat()) * m, p1.lng() + (p2.lng() - p1.lng()) * m);
}
// === A method which returns the Vertex number at a given distance along the path ===
// === Returns null if the path is shorter than the specified distance ===
google.maps.Polyline.prototype.GetIndexAtDistance = function(metres) {
// some awkward special cases
if (metres == 0) return this.getPath().getAt(0);
if (metres < 0) return null;
var dist = 0;
var olddist = 0;
for (var i = 1;
(i < this.getPath().getLength() && dist < metres); i++) {
olddist = dist;
dist += google.maps.geometry.spherical.computeDistanceBetween(this.getPath().getAt(i), this.getPath().getAt(i - 1));
}
if (dist < metres) {
return null;
}
return i;
}
html,
body,
#map_canvas {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px
}
<script src="https://maps.googleapis.com/maps/api/js?libraries=geometry&key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk"></script>
<input type="button" value="restart animation" onclick="startAnimation()" />
<div id="map_canvas"></div>

Resources