I have a map which is a drilldown map. It goes from continents view to a country view.
My goal is to place markers dynamically based on the selected country (after the drilldown).
Here in this example I want to place a marker in Berlin (Germany) however this marker doesn't get created.
Example: https://codepen.io/ms92o/pen/gjMPEJ?editors=1111
var map = AmCharts.makeChart("chartdiv", {
"type": "map",
"theme": "light",
"areasSettings": {
"autoZoom": true,
"rollOverOutlineColor": "#9a7bca",
"selectedColor": "#9a7bca",
"color": "#a791b4",
"rollOverColor": "#9a7bca"
},
"zoomControl": {
"buttonFillColor": "#a6bd7f",
"buttonRollOverColor": "#9a7bca"
},
"dataProvider": continentsDataProvider,
"objectList": {
"container": "listdiv"
},
"listeners": [{
"event": "clickMapObject",
"method": function (event) {
console.log(event);
// TODO: how to create some markers here based on the selected country?
let rep = { title: 'Berin', latitude: '52.520', longitude: '13.409779' };
rep.svgPath = targetSVG;
rep.zoomLevel = 3;
rep.scale = 1.2;
rep.label = rep.title;
map.dataProvider.images.push(rep);
}
}]
});
You need to call the map's validateNow()/validateData() methods whenever you update the map with new areas/markers or changes to its properties. The caveat of these calls is that they reset the map's zoom position unless you modify the dataProvider's zoom properties (zoomLevel, zoomLatitude and zoomLongitude), which also affects the home button unless you reset them after the fact.
Here's a solution that adds the marker while making sure the zoom level sticks and fixes the home button afterward:
"listeners": [{
"event": "clickMapObject",
"method": function (event) {
let rep = { title: 'Berin', latitude: '52.520', longitude: '13.409779' };
rep.svgPath = targetSVG;
rep.zoomLevel = 3;
rep.scale = 1.2;
rep.label = rep.title;
map.dataProvider.images.push(rep);
//delay the update so that the click+zoom action still occurs before
//adding the marker
setTimeout(function() {
//preserve current zoom level on update
map.dataProvider.zoomLevel = map.zoomLevel();
map.dataProvider.zoomLatitude = map.zoomLatitude();
map.dataProvider.zoomLongitude = map.zoomLongitude();
map.validateNow(); //add marker
//reset the zoom values so that the home button zooms
//completely out when clicked
map.dataProvider.zoomLevel = 0;
map.dataProvider.zoomLatitude = undefined;
map.dataProvider.zoomLongitude = undefined;
}, (map.zoomDuration + .5) * 1000);
}
}]
Updated codepen
Related
I am using
vue-google-maps
They working good so far, I want to achieve that when someone search and select their area a marker appears and then they can drag it to their required position.
I have so far managed to make the marker draggable by editing GoogleMap.vue file
<gmap-marker
:key="index"
v-for="(m, index) in markers"
:position="m.position"
:draggable="true"
#click="center=m.position"
#drag="setCurrent(this)"
></gmap-marker>
Now I am able to drag the marker however the coordinates (lat:long) doesn't change.
I am using Laravel 1.4.1
Vue 3.0.0-beta.6
Please help
rest of the GoogleMap.vue look like this
<script>
export default {
name: "GoogleMap",
data() {
return {
center: { lat: 24.9004057, lng: 67.1926178 },
markers: [],
places: [],
currentPlace: null,
};
},
mounted() {
this.geolocate();
},
methods: {
// receives a place object via the autocomplete component
setPlace(place) {
this.currentPlace = place;
this.addMarker();
},
addMarker() {
if (this.currentPlace) {
const marker = {
lat: this.currentPlace.geometry.location.lat(),
lng: this.currentPlace.geometry.location.lng()
};
this.markers.push({ position: marker, draggable: true });
this.places.push(this.currentPlace);
this.center = marker;
this.currentPlace = null;
}
},
geolocate: function() {
navigator.geolocation.getCurrentPosition(position => {
this.center = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
});
},
checkthis(position){
console.log(navigator.position.getCurrentPosition());
}
}
};
</script>
To get marker position once it is dragged Marker.dragend is better suited then Marker.drag:
This event is fired when the user stops dragging the marker.
In case of vue-google-maps library marker position could be determined like this:
<gmap-marker
:draggable="true"
:key="index"
v-for="(m, index) in markers"
:position="m.position"
#click="center=m.position"
#dragend="showLocation"
></gmap-marker>
showLocation: function(evt){
console.log( evt.latLng.toString());
}
where evt is a MouseEvent object and MouseEvent.latLng property contains the position of dragged marker
The same applies to #drag event.
When I click on a country the zoomlevelchanged event is fired a hundred times! I expect that it fires a maximum of 2 times.
Is that intentional or did I do something wrong?
I wanted to provide a codepen sample post however it will break if I insert the code below:
chart.events.on("zoomlevelchanged", function(e) {
console.log(e);
});
The event shouldn't be triggered that often in my opinion because it will take some resource if it needs to compute s.th. based on the zoom level.
Is it possible to use another event to react when somebody zoomed in the map? It shouldn't be bound by the direction of the zoom like up or down and also it shouldn't be different between mouse and smartphone inputs.
The complete code:
import { Component, NgZone, AfterViewInit, OnDestroy } from "#angular/core";
import * as am4core from "#amcharts/amcharts4/core";
import * as am4maps from "#amcharts/amcharts4/maps";
import am4geodata_worldLow from "#amcharts/amcharts4-geodata/worldLow";
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit, OnDestroy {
private chart: am4maps.MapChart;
constructor(private zone: NgZone) { }
ngAfterViewInit() {
this.zone.runOutsideAngular(() => {
// create map instance
let chart = am4core.create("mapdiv", am4maps.MapChart);
// set map definition
chart.geodata = am4geodata_worldLow;
// set projection
chart.projection = new am4maps.projections.Miller();
// Create map polygon series
var polygonSeries = chart.series.push(new am4maps.MapPolygonSeries());
// Configure series
let polygonTemplate = this.configureTemplate(polygonSeries.mapPolygons.template);
// Make map load polygon (like country names) from GeoJSON
polygonSeries.useGeodata = true;
// Use data instead for additional information
polygonSeries.data = [{
"id": "US",
"name": "United States",
"value": 100,
"fill": am4core.color("#F05C5C")
}, {
"id": "FR",
"name": "France",
"value": 50,
"fill": am4core.color("#5C5CFF")
}];
polygonTemplate.propertyFields.fill = "fill";
// exclude antarktika
polygonSeries.exclude = ["AQ"];
// include germany only
// polygonSeries.include = ["DE"];
// ImageSeries (for adding visual objects like markers)
let imageSeries = chart.series.push(new am4maps.MapImageSeries());
// Basic Circle Image
let imageSeriesTemplate = imageSeries.mapImages.template;
let circle = imageSeriesTemplate.createChild(am4core.Circle);
circle.radius = 4;
circle.fill = am4core.color("#B27799");
circle.stroke = am4core.color("#FFFFFF");
circle.strokeWidth = 2;
circle.nonScaling = true;
circle.tooltipText = "{title}";
// Binding Marker Properties to Data
imageSeriesTemplate.propertyFields.latitude = "latitude";
imageSeriesTemplate.propertyFields.longitude = "longitude";
// Adding 3 Markers to the Map
imageSeries.data = [{
"latitude": 48.856614,
"longitude": 2.352222,
"title": "Paris"
}, {
"latitude": 40.712775,
"longitude": -74.005973,
"title": "New York"
}, {
"latitude": 49.282729,
"longitude": -123.120738,
"title": "Vancouver"
}];
// Adding Zoom Controls
// chart.zoomControl = new am4maps.ZoomControl();
// Heat map based on the value property in data
// polygonSeries.heatRules.push({
// "property": "fill",
// "target": polygonSeries.mapPolygons.template,
// "min": am4core.color("#ffffff"),
// "max": am4core.color("#AAAA00")
// });
chart.events.on("zoomlevelchanged", function(e) {
console.log(e);
});
// chart code goes here
this.chart = chart;
});
}
configureTemplate(polygonTemplate) {
// Create ToolTip
polygonTemplate.tooltipText = "{name}";
// Fill Template Color
polygonTemplate.fill = am4core.color("#BADA55");
// Create hover state
let hoverState = polygonTemplate.states.create("hover");
// set alternative Fill Color when hovering
hoverState.properties.fill = am4core.color("#367B25");
// show additional data in tooltip, akquired from polygonSeries.data
polygonTemplate.tooltipText = "{name}: {value}";
// Zooming to map area on click
polygonTemplate.events.on("hit", function(ev){
ev.target.series.chart.zoomToMapObject(ev.target)
});
return polygonTemplate;
}
ngOnDestroy() {
this.zone.runOutsideAngular(() => {
if (this.chart) {
this.chart.dispose();
}
});
}
}
I would like to show sum of multiple values as one chart output with amCharts. I am using dataLoader with JSON to get the data. I know I have to create a function for but I couldn't understand how to get the data from the dataLoader to calculate
{
"balloonText": "[[title]] of [[valueAxis]]:[[value]]",
"lineThickness": 3,
"id": "sumValue",
"title": "sum Value",
"valueField": (function() {
var sumValues = "calculation";
return sumValues
}
this attempt is probably not correct but this is how I started
{
"balloonText": "[[title]] of [[valueAxis]]:[[value]]",
"lineThickness": 3,
"id": "LoadigTime",
"title": "Loadig Time",
"valueField": (function() {
var sumValues = (HomePageLoad + LoginToParametersLoad + ParametersLoad + AlarmsLoad + SwitchSideLoad + LoginToAdminLoad + AdminLoad) / 7;
return sumValues
})
}
valueField cannot be a function, only a string reference to a field in your data.
If the chart is meant to be displaying the sum of all of those fields in your data as a chart, simply add logic to your postProcess callback to create a new dataset containing your sums, e.g.
postProcess: function(data) {
var newData = [];
data.forEach(function(dataItem) {
var item = {
YOUR_CATEGORY_FIELD: dataItem.YOUR_CATEGORY_FIELD, //replace with your category field name
sum: 0
};
//loop through your item's keys and sum everything up, filtering out
//your category property
item.sum = Object.keys(dataItem).reduce(function(sum, key) {
if (key !== "YOUR_CATEGORY_FIELD") {
sum += dataItem[key]
}
return sum;
}, 0);
newData.push(item);
});
return newData;
},
// ...
graphs: [{
valueField: "sum",
// other props here
}]
i want to create a real time chart, using Kendo ui chart and signalr. I see this example, but has no code. So i try alone.
A little demonstration of my code:
At first I created a kendo chart
function queueActivityChart() {
$("#queueActivityChart").kendoChart({
legend: {
visible: true
},
seriesDefaults: {
labels: {
visible: true,
format: "{0}",
background: "transparent"
}
},
series: [{
type: "line",
field: "Incoming",
categoryField: "DateTime",
}],
valueAxis: {
labels: {
format: "{0}"
},
line: {
visible: false
}
},
categoryAxis: {
labels:
{
rotation: -90,
dateFormats:
{
seconds: "ss",
minutes: "HH:mm:ss",
hours: "HH:mm",
days: "dd/MM",
months: "MMM 'yy",
years: "yyyy"
}
}, type: "Date", field: "DateTime", baseUnit: "seconds"
}
});
var chart = $("#queueActivityChart").data("kendoChart");
chart.options.transitions = false;
}
$(document).ready(queueActivityChart);
$(document).bind("kendo:skinChange", queueActivityChart);
Then I have this part of code, that get from server data
$scope.signalRData = [];
$scope.signalR.on('receiveCounters', function (data) {
$scope.queueData = data;
for (var i = 0; i < data.length; i++) {
$scope.signalRData.push(data[i]);
}
while ($scope.signalRData.length > 12) {
$scope.signalRData.splice(0, 1);
}
$("#queueActivityChart").data("kendoChart").setDataSource(
new kendo.data.DataSource({
group: {
field: "Name"
},
data: $scope.signalRData
}));
});
This works! And I get a picture of the latest updated items.
But the problem is that this chart is like to put one picture in front of other. I mean that this is the first time that load Data Source; that creates a chart of my data, the second time my data has changed, some values are still in my array some others has move out, the third too.
It seems like it puts a picture of my current data in front of the
previous data. It's not smoothie and cannot use chart's legend
property because I initialize my Data Source everytime.
Can someone help me how can create a smoothie kendo chart with real time data like the kendo official example? Also can somehow to add scroller to bottom?
I looked at the code for the benchmark and I think you may be missing in your chart which is renderAs: "canvas"
Also, in the example, the data is kept locally (saved) and then moved so it creates that "smooth" effect you may be talking about.
Here is the code that you can be of interest:
function step() {
addPoint();
$("#chart").data("kendoChart").refresh();
frames++;
if (playing) {
kendo.animationFrame(step);
}
}
function addPoint() {
var stockData,
change,
lastValue;
// Shift existing categories to the left and add the next date at the end
lastDate = new Date(lastDate.getTime() + TICKS_PER_DAY);
categoryList.push((lastDate.getMonth() + 1) + "/" + (lastDate.getDay() + 1));
if (categoryList.length > POINTS) {
categoryList.shift();
}
for (var i = 0; i < stocks.length; i++) {
stockData = stocks[i];
change = (Math.random() > 0.5 ? 1 : - 1) * Math.random() * 10;
lastValue = stockData[stockData.length - 1] || Math.random() * 10;
// Add a new pseudo-random data point
stockData.push(Math.min((i + 1) * 20, Math.max((i + 1) * 10, lastValue + change)));
// Shift the data points of each series to the left
if (stockData.length > POINTS) {
stockData.shift();
}
}
}
Check out the source code of your example for the full source code and use the dojo to test our their code and play around with it easily
I made a Highstock diagramm and got aproblem with zooming on the yAxis.
I have a Button and 2 textfield to get the wanted min/max values for the axis. With min:0, max: 100 it works well. With min:0, max:80 it doesn't (max will still be 100 in the Diagramm).
If I use the mouse for zooming it works well (even a min of: 3.7 and a max of 3.894 is possible). But using the mouse is not an Option, because in the later Diagramm there will be 3 yAxes with individual zoom.
$(function () {
var seriesOptions = [],
seriesCounter = 0,
names = ['MSFT', 'AAPL', 'GOOG'];
/**
* Create the chart when all data is loaded
* #returns {undefined}
*/
function createChart() {
$('#container').highcharts('StockChart', {
rangeSelector: {
selected: 4
},
chart:{
zoomType: 'xy'
},
yAxis: [
{
labels: {
format: '{value}',
},
height: '100%',
opposite: false,
plotLines: [{
value: 0,
width: 2,
color: 'silver'
}]
},
],
plotOptions: {
series: {
compare: 'percent'
}
},
tooltip: {
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>',
valueDecimals: 2
},
series: seriesOptions
},
function(chart){
$('#btn').click(function(){
var min = temp_min.value,
max = temp_max.value;
chart.yAxis[0].setExtremes((min),(max));
});
});
}
$.each(names, function (i, name) {
$.getJSON('https://www.highcharts.com/samples/data/jsonp.php?filename=' + name.toLowerCase() + '-c.json&callback=?', function (data) {
if(seriesCounter==0){
seriesOptions[i] = {
name: name,
data: data,
yAxis: 0
};
} else {
seriesOptions[i] = {
name: name,
data: data,
yAxis: 0
};
}
// As we're loading the data asynchronously, we don't know what order it will arrive. So
// we keep a counter and create the chart when all the data is loaded.
seriesCounter += 1;
if (seriesCounter === names.length) {
createChart();
}
});
});
});
JSFiddle
Another Question: Is it possible to set up a scrollbar for the yAxis as well?
Thanks for your help, Patrick
This is related with fact that tickInterval is not regular, so is rounded to value (like 100). The solution is using tickPositioner which calculates ticks, based on extremes which you define.
tickPositioner: function (min,max) {
var positions = [],
tick = Math.floor(min),
increment = Math.ceil((max - min) / 5);
for (tick; tick - increment <= max; tick += increment) {
positions.push(tick);
}
return positions;
},
http://jsfiddle.net/6s11kcwd/
The scrollbar is supported only for xAxis.