I have a set of values like this ones:
...
[
{
"LATITUDE": "40.1733",
"LONGITUDE": "-85.4786",
"EVENT_ID_COUNT": "2"
},{
"LATITUDE": "40.1733",
"LONGITUDE": "-85.4786",
"EVENT_ID_COUNT": "5"
}
]
Nokia Maps identify each record from this json as only one marker, but, what I need is to show the "EVENT_ID_COUNT" value, and add together them.
For example, if I am out, I should see one marker with value "7", but when I zoom in, I should see two markers, where one of them has "2" and the other "5"
This behavior helps me to have 100K markers, but only show 3K because several of them come from the same place.
The code:
....
function ZoomToTheme(){
var baseTheme = new nokia.maps.clustering.MarkerTheme();
this.getClusterPresentation= function(dataPoints){
var cluster = baseTheme.getClusterPresentation(dataPoints);
cluster.$boundingBox = dataPoints.getBounds();
return cluster;
}
this.getNoisePresentation = function(dataPoint){
var noisePoint = baseTheme.getNoisePresentation(dataPoint);
noisePoint.$text = dataPoint.text;
return noisePoint;
}
}
function addZoomToListener(map){
map.addListener("click", function(evt) {
if ( evt.target.$boundingBox !== undefined){
evt.display.zoomTo(evt.target.$boundingBox, false);
$("#ticker").text("");
} else if ( evt.target.$text !== undefined){
$("#ticker").text(evt.target.$text + " noise point was clicked.");
}
} );
}
....
data.forEach(function(d, index) {
if(d[mapOptions.count]){
dataPoints.push({
latitude : d.latitude,
longitude : d.longitude,
text : d.count
});
}
}, this);
...
this.clusterProvider = new nokia.maps.clustering.ClusterProvider(
this.map,
{
eps: 16,
minPts: 1,
dataPoints: dataPoints,
theme : new ZoomToTheme()
}
);
You can add the event count to each of the data points as 'weight' property (https://developer.here.com/javascript-apis/documentation/v3/maps/topics_api_nlp/h-clustering-datapoint.html). The result will be that they get counted together during the clustering. After the clustering operation your clusters should have the combined weight (https://developer.here.com/javascript-apis/documentation/v3/maps/topics_api_nlp/h-clustering-icluster.html). Use getWeight on the result to get the combined weight within a cluster...
Related
https://www.amcharts.com/demos/custom-html-elements-map-markers/
I need to add a location marker when I click a button. I tried imageSeries.data.push, addData, init and other methods but when I move the chart (mappositionchanged) is triggered location updates.
I need to make it work automatically without moving or zooming the chart.
I am using amcharts version 4.
function test() {
imageSeries.addData({
"zoomLevel": 5,
"scale": 0.5,
"title": "Pretoria",
"latitude": -25.7463,
"longitude": 28.1876
});
alert(imageSeries.data);
}
<button onClick="test();">click</button>
I'm unable to replicate this behavior where adding data dynamically to a MapImageSeries via its addData method requires refreshing the chart (for what it's worth, user posted the same issue to our GitHub and solved it there.). imageSeries.addData(...) should work just fine.
Setup code:
// Create map instance
var chart = am4core.create("chartdiv", am4maps.MapChart);
// Set map definition
chart.geodata = am4geodata_worldLow;
// Create map polygon series
var polygonSeries = chart.series.push(new am4maps.MapPolygonSeries());
// Make map load polygon (like country names) data from GeoJSON
polygonSeries.useGeodata = true;
// Create image series
var imageSeries = chart.series.push(new am4maps.MapImageSeries());
// Create a circle image in image series template so it gets replicated to all new images
var imageSeriesTemplate = imageSeries.mapImages.template;
var 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}";
// Set property fields
imageSeriesTemplate.propertyFields.latitude = "latitude";
imageSeriesTemplate.propertyFields.longitude = "longitude";
Test code (addPlace method):
// Add data for the three cities
var data = [{
"latitude": 48.856614,
"longitude": 2.352222,
"title": "Paris",
zoomLevel: 1
}, {
"latitude": 40.712775,
"longitude": -74.005973,
"title": "New York",
zoomLevel: 2
}, {
"latitude": 49.282729,
"longitude": -123.120738,
"title": "Vancouver",
zoomLevel: 4
}];
const dataIterator = data[Symbol.iterator]();
function addPlace() {
var item = dataIterator.next();
if ( !item.done) {
imageSeries.addData(item.value);
}
}
Here's a quick demo:
https://codepen.io/team/amcharts/pen/c5a5803d81b9517a8fd37d4e2c6541ed
The "Add Marker" button adds a marker each time (up to 3 times since there's only 3 items in the array) without having to refresh the chart (via invalidate or whatever).
I want to sort array/object of cities based on my location from nearest (to my location) to furthest
I have list of locations that i get from database
How would i solve this using javascript and HTML5 geolocation?
I have something like this:
example :
var locations= [{"name":"location1" "latitude" :"31.413165123"
"longitude":"40.34215241"},{"name":"location2" "latitude" :"31.413775453"
"longitude":"40.34675341"}]
and I want to sort those location by nearest to my location
FIRST: The provided array is broken (i added commas between fields).
var locations = [{
"name": "location1",
"latitude": "31.413165123",
"longitude": "40.34215241"
}, {
"name": "location2",
"latitude": "31.413775453",
"longitude": "40.34675341"
}];
You'll need to leverage a custom sort function, which needs to return 1, -1, or 0 based on comparing 2 items.
var myLong = 42.0; // whatever your location is
var myLat = 3.16; // whatever your location is
locations.sort( function (a, b) {
// This is untested example logic to
// help point you in the right direction.
var diffA = (Number(a.latitude) - myLat) + (Number(a.longitude) - myLong);
var diffB = (Number(b.latitude) - myLat) + (Number(b.longitude) - myLong);
if(diffA > diffB){
return 1;
} else if(diffA < diffB){
return -1;
} else {
return 0; // same
}
} );
Store your location, create a function that calculates the distance between two points and then use the sort method:
function dist({latitude: lat1, longitude: long1}, {latitude: lat2, longitude: long2}) {
// I'm not very good at geography so I don't know how to calculate exactly the distance given latitudes and longitudes.
// I hope you can figure it out
// the function must return a number representing the distance
}
navigator.geolocation.getCurrentPosition(({coords}) => {
coords.latitude = parseFloat(coords.latitude)
corrds.longitude = parseFloat(coords.longitude)
locations.sort((p1, p2) => dist(coords, {latitude: parseFloat(p1.latitude), longitude: parseFloat (p1.longitude)}) -
dist(coords, {latitude: parseFloat(p2.latitude), longitude: parseFloat(p2.longitude)}))
})
Hope it helps you
I have modified the ESRI ArcGIS js API Measurement widget to keep a session based history of the measurements the user has made. When a user clicks on a history item, it should display the geometry associated with that history item as a GraphicsLayer on the map. I am using knockout to manage the history items and to retrieve measurement metadata when a history item is clicked.
At this point, both my Polygons (for area) and Points (for location) work just fine with the SimpleFillSymbol() and the SimpleMarkerSymbol(), respectively. However, the Polyline geometry returned from a distance measurement is not displaying on the map with the SimpleLineSymbol().
Here's the code:
var graphicLayerId = "measurementHistoryGraphicsLayer";
function addGraphicsLayerToMap(graphicsLayer) {
var lay = getGraphicsLayerFromMap();
if (lay !== undefined) {
lay.clear();
lay.add(graphicsLayer);
map.removeLayer(lay);
}
map.addLayer(graphicsLayer);
}
function createGraphicFromGeometry(viewModel) {
//Determine the symbol type
var symbol;
switch (viewModel.activeTool) {
case "area":
symbol = new esri.symbol.SimpleFillSymbol(esri.symbol.SimpleFillSymbol.STYLE_SOLID,
new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new esri.Color([255, 0, 0]), 2),
new esri.Color([255, 0, 0, 0.25]));
break;
case "distance":
symbol = new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new esri.Color([255, 0, 0]), 3);
break;
case "location":
symbol = new esri.symbol.SimpleMarkerSymbol(esri.symbol.SimpleMarkerSymbol.STYLE_SQUARE, 10,
new esri.symbol.SimpleLineSymbol(esri.symbol.SimpleLineSymbol.STYLE_SOLID, new esri.Color([255, 0, 0]), 3),
new esri.Color([0, 255, 0, 0.25]));
break;
}
var graphic = new esri.Graphic(viewModel.geometry, symbol, { "extent": viewModel.extent, "unitName": viewModel.unitName });
return graphic;
}
function createGraphicsLayerFromGraphic(graphic) {
var graphicLayer = new esri.layers.GraphicsLayer({ id: graphicLayerId });
graphicLayer.add(graphic);
graphicLayer.setRenderer(new esri.renderer.SimpleRenderer(graphic.symbol));
return graphicLayer;
}
function getGraphicsLayerFromMap() {
return map.getLayer(graphicLayerId);
}
$(document).on('click', '#emv_measurement_history .list-group-item', function () {
$('#emv_measurement_history .list-group-item.list-group-item-info').removeClass('list-group-item-info');
$(this).addClass('list-group-item-info');
var measurementData = ko.mapping.toJS(ko.dataFor($(this)[0]));
var graphic = createGraphicFromGeometry(measurementData);
var graphicsLayer = createGraphicsLayerFromGraphic(graphic);
addGraphicsLayerToMap(graphicsLayer);
map.setExtent(measurementData.extent);
});
Like I said, this works fine for both area and location, but distance does not seem to work. I've even tried adding a hard-coded polyline value in there are creating a SimpleLineSymbol from that without success.
For additional information, here is the Polyline info:
[
[
[
2591769.2297164765,
5236836.417134136
],
[
2573584.2281166334,
4620357.96034264
],
[
2557384.1428811993,
4038303.8136230526
],
[
3124973.8484519687,
4260007.60486125
],
[
3714518.451309448,
4454862.77067183
],
[
4324318.833989203,
4618552.510359674
],
[
4666465.839330839,
4693607.843734423
],
[
5013294.285789721,
4757423.375729576
]
]
]
And the spatial reference is set to 102100.
I finally figured it out.
I have the geometry from the original measurement stored in a knockout variable. When I was reading from it, it would build out the graphic, symbol, and graphic layer just fine without any errors throwing.
I discovered that for some reason, the data and the spatial reference were mismatched, so I extracted the path from the stored geometry, assigned it to a new polyline variable, re-set the spatial reference to 102100 like I needed, and re-assigned the geometry to the graphic, which worked.
var g = new esri.Graphic(viewModel.geometry, symbol, { "extent": viewModel.extent, "unitName": viewModel.unitName });
if (viewModel.activeTool === "distance") {
var polyline = new esri.geometry.Polyline(viewModel.geometry.paths);
polyline.setSpatialReference(new esri.SpatialReference(102100));
g.setGeometry(polyline);
}
Given the following code for Mapbox, whereby I am plotting points and polylines between a number of points. User will choose a different selection and the results of that will replace the pins on the map. These pins will then be drawn together in the correct order using polylines.
$.post('_posts/get-pins.php', {traveller: $(this).val()}, function(data){
var featureLayer = L.mapbox.featureLayer().addTo(map);
var featureCollection = {
"type": "FeatureCollection",
"features": []
};
var lineArray = [];
$.each(data, function (k, item) {
featureCollection.features.push({
"type": "Feature",
"properties": {
"id": item.id,
"title": item.title,
"description": item.description,
"image": item.image,
"marker-symbol": "star",
"marker-color": "#ff8888",
"marker-size": "large"
},
"geometry": {
"type": "Point",
"coordinates": [
item.long,
item.lat
]
}
});
lineArray[item.id] = [item.lat, item.long];
});
featureLayer.setGeoJSON(featureCollection);
lineArray = lineArray.filter(function(){return true});
var polyline = L.polyline(lineArray).addTo(map);
},'json');
So I need to remove the polylines and the markers before plotting the new ones. I have tried numerous combinations of map.removeLayer(xxx) replacing xxx with many of the variables that are being created but all I have managed to do is remove the markers. It just leaves the polylines intact and just stacks the polyline layers.
Declare your variables for the featureLayer and polyline outside of the method/function you are using to update them:
var featureLayer, polyline;
In the function, check if the variable featureLayer already is an instance of FeatureLayer then clear it's layers, if it's not create the new layer:
if (featureLayer instanceof L.mapbox.FeatureLayer) {
featureLayer.clearLayers();
} else {
featureLayer = L.mapbox.featureLayer().addTo(map);
}
With the polyline you got to do it differently because it hasn't got a function to clear al the added points, just check if it's an instance of L.Polyline, if so remove it from the map using L.Map's removeLayer method and afterwards just define a new polyline:
if (polyline instanceof L.Polyline) {
map.removeLayer(polyline);
}
polyline = L.polyline([]).addTo(map);
Working example on Plunker: http://plnkr.co/edit/7nlgiA50NuPGsQOF0Fv4?p=preview
What i have done so far:
i'm developing an application where i have to display more than(50K) points/Markers on the Navteq map divided into different segments.
for example: if i have 50K points i will divide all points into different segments.
if i divide 50K points into 50 segments each segment would have 1000 points (may not be 50 segments , it may depend).
right now it is working but it takes long time and hangs to render all the points on the MAP.so that i would like to perform segmentation displaying to display only few points with clustering.
so that i can get an idea of how the segment will look like.
but the problem here is i should only perform the clustering based on the segments.otherwise points from different segments willbe mixed together and displayed
as single unit and that conveys the wrong information to the user.
so here my question is: is it possible to perform the clustering based on the segment. so that only points from same segment will be clustered.
Note: if this is not possible, i would like to use Latest version of here-maps 2.5.3 (Asynchronous) may reduce some time while loading, so that i would like to use indexing functionality also while rendering the points
to improve the rendering time using nokia.maps.clustering.Index class.
i studied that indexing would reduce the time while rendering the points/markers on map. does it help in my case? could anybody please suggest how to perform indexing ?
This is the code with which i'm displaying points on map:
function displayAllLightPoints(arrLightPointCoordinats, totalLightPoints,
selectedSegmentId, totalSegmentsCount,segmentColorcode)
{
var MyTheme1 = function () {
};
segmentColorcode = segmentColorcode.substring(2,segmentColorcode.length-1);
MyTheme1.prototype.getNoisePresentation = function (dataPoint) {
var markerLightPoint = new nokia.maps.map.Marker(dataPoint, {
icon: new nokia.maps.gfx.BitmapImage("..//Images//Lightpoint//" +
segmentColorcode + ".png"),
anchor: {
x: 12,
y: 12
}
});
return markerLightPoint;
};
MyTheme1.prototype.getClusterPresentation = function (data) {
var markerLightPoint = new
nokia.maps.map.StandardMarker(data.getBounds().getCenter(), {
icon: new nokia.maps.gfx.BitmapImage("..//Images//
Segment/" + segmentColorcode + ".png", null, 66, 65),
text: data.getSize(),
zIndex: 2,
anchor: {
x: 12,
y: 12
}
});
return markerLightPoint;
};
var ClusterProvider = nokia.maps.clustering.ClusterProvider,
theme = new MyTheme1(),
clusterProvider = new ClusterProvider(map, {
eps: 0.00000000001,
minPts: 1000000,
strategy: nokia.maps.clustering.ClusterProvider.
STRATEGY_DENSITY_BASED,
theme: theme,
dataPoints: []
});
var lightpointsDataSet1 = new Array();
for (var i = 0; i < totalLightPoints; i++) {
lightpointsDataSet1[i] = { latitude: arrLightPointCoordinats[i][0],
longitude: arrLightPointCoordinats[i][1], title:
'LightPoint ' + (i + 1) };
}
clusterProvider.addAll(lightpointsDataSet1);
clusterProvider.cluster();
}
To deal with a very large (50K+) data set , I would do all the heavy number crunching server side and send over a new JSON response whenever the map is updated. Something like the HTML page described here
The key section of the code is the ZoomObserver:
var zoomObserver = function (obj, key, newValue, oldValue) {
zoom = newValue;
if (zoom < 7)
{ zoom = 7;}
if (zoom > 16)
{ zoom = 16;}
// Define the XML filename to read that contains the marker data
placeMarkersOnMaps('http://api.maps.nokia.com/downloads/java-me/cluster/'+ zoom + '.xml'
+ '?lat1=' + map.getViewBounds().topLeft.latitude
+ '&lng1='+ map.getViewBounds().topLeft.longitude
+ '&lat2='+ map.getViewBounds().bottomRight.latitude
+ '&lng2='+ map.getViewBounds().bottomRight.longitude);
};
map.addObserver("zoomLevel", zoomObserver );
Where the REST service returns a "well-known" data format which can be used to add markers and clusters to the map.
Now assuming you have two massive data sets you could make two requests to different endpoints, or somehow distinguish which cluster of data belongs to which so that you would just be returning information of the form:
{latitude':51.761,'longitude':14.33128,'value':102091},
i.e. using the DataPoint standard (which means you could use a heat map as well.
Of course, what I'm not showing here is the back-end functionality to cluster in the first place - but this leaves the client (and the API) to do what it does best displaying data, not number crunching.