Update/Reload specific tile in leaflet dynamicly - javascript

If my server tells the clients when and what tile to reload/update, how can reload/update the tile sent from the server? I'm using the L.CRS.Simple CRS. And I have no zoom levels on a custom game map.
Here is my code:
var map = L.map('map', {
crs: L.CRS.Simple,
attributionControl: false
}).setView([0, 0], 2)
L.tileLayer('/chunks/{x}.{y}.png', {
maxNativeZoom: 1,
minNativeZoom: 1,
}).addTo(map)
function ReloadTile(x,y){
// UPDATE TILE HERE Request for example /chunks/{1}.{1}.png depending on input
}

First, listen to the tileload and tileunload events of the L.TileLayer to grab references to tiles as they load (and free those references as they unload), e.g.
let myTileLayer = L.tileLayer(...);
let tileRefs = {};
myTileLayer.on('tileload', function(ev) {
tileRefs[ ev.coords.x + ',' + ev.coords.y ] = ev.tile;
});
myTileLayer.on('tileunload', function(ev) {
delete tileRefs[ ev.coords.x + ',' + ev.coords.y ];
});
...so whenever you want to update a tile, you just have to search for it in the data structure.
Remember that in order to reload a HTMLImageElement, you've got to change its src property, e.g.:
function ReloadTile(x,y){
const tile = tileRefs[x + ',' + y];
if (!tile) {
// Tile is not currently loaded on the screen
return;
}
tile.src = "/chunks/{1}.{1}.png";
}
Beware of requesting a URL that your browser has cached. Do your homework regarding cache busting and relevant HTTP headers.
Note that I'm using a javascript Object as key-value data structure, and string concatenation to build up a unique key per tile. You're free to use other data structures (such a Map) and any other method to index the tiles (such as a double-depth data structure for x-y, or triple-depth for x-y-z, or indexing by tile URL). Also note that the sample code is usign only the X and Y coordinates of the tile since your TileLayer seems to have only one zoom level.

lThanks for the help!
My final code:
var map = L.map('map', {
crs: L.CRS.Simple,
attributionControl: false
}).setView([0, 0], 2)
const path = '/chunks/{x}.{y}.png?cash={time}'
const layer = L.tileLayer(path, {
maxNativeZoom: 1,
minNativeZoom: 1,
time: 0
}).addTo(map)
function VectorToString(vector) {
return vector.x + "." + vector.y
}
class TileHandler {
constructor(layer){
this.layer = layer
this.layers = {}
this.layer.on('tileload', (tile) => {
this.layers[VectorToString(tile.coords)] = tile
})
this.layer.on('tileunload', (tile) => {
delete this.layers[VectorToString(tile.coords)]
})
}
update(position){
const tile = this.layers[VectorToString(position)]
const url = L.Util.template(tile.target._url, {
...position,
time: new Date().getTime()
})
if (!tile) return
tile.tile.src = url
}
}

Related

How to insert node into polyline link in gojs, preserving positions of points in links either side of the new node

I asked a question on StackOverflow (Seeking Javascript library for displaying and editing networks of nodes and edges) and was pointed at the gojs splice sample.
This has got me a long way, so thanks for the answer, but I have run into a brick wall trying to get the behaviour I want.
The app I am trying to create is to edit the borders on a map:
node = place where three or borders meet
link = segment of border between two nodes.
Hence, the nodes and links are unlabelled (nodes are just small circles, links are just polylines).
I have attempted to adapt the splice sample (https://gojs.net/extras/splicing.html) appropriately. The key features I need over and above what the sample does are:
choosing exactly where to position the new node on the link between the existing ones
preserving the shape of the polylines.
(The existing sample puts the new node equidistant between the existing ones and uses straight links.)
The user experience I have tried to create is this: first, you select the link, so it gets its usual adornments; then you shift-click on one of the adornments at a point on the polyline and that point becomes the new node.
I have sought to do this by overriding methods of the LinkReshapingTool using the extension mechanism described at https://gojs.net/latest/intro/extensions.html (rather than creating a subclass).
Whatever I have tried, though, I can't get the polylines to stay. By inspecting the diagram data model in the Chrome DevTools debugger after my code has run, it appears that it is correct (i.e. I can see the correct array of points in the links). However, when I then allow execution to continue the links do not display as expected (they are straight), and if I subsequently look at the data model then the multiple points have disappeared and each link just has a start and end.
I have tried various things, without success, for example:
deferring the splicing till after the tool has completed
passing the points into the modified links in different ways (array v list v string)
putting the processing into different overridden methods.
My current code is below. Please excuse crass stylistic faux pas - I am not an experienced JavaScript programmer.
<!DOCTYPE html> <!-- HTML5 document type -->
<!--
Adapted from splicing example from gojs.net
-->
<html>
<head>
<!-- use go-debug.js when developing and go.js when deploying -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gojs/1.8.28/go-debug.js"></script>
</head>
<body>
<div id="myDiagramDiv"
style="width:400px; height:300px; background-color: #DAE4E4;"></div>
<script>
var $ = go.GraphObject.make;
// state variables to remember what to do after reshaping tool aborted
var doSpliceNode = false;
var doSpliceIntoLink = null;
var doSplicePoint = null;
var doSpliceIndex = -1;
// diagram
var myDiagram = $(go.Diagram, "myDiagramDiv",
{
"undoManager.isEnabled": true
});
var tool = myDiagram.toolManager.linkReshapingTool;
// Override doMouseDown on linkreshapingtool. If user clicks on a handle with SHIFT pressed, want to insert
// a new node at that point rather than use the default behaviour to (further) reshape the link, and moreover
// want to retain the points in the link. I.e. turn one of the points in the link into a new node.
// (Existing gojs splicing example does not do this: it just puts a new node at the midpoint of the existing
// link, with no regard to the points along the link.)
tool.doMouseDown = function() {
console.log("mousedown at (" + this.Fp.M + "," + this.Fp.N + ")");
console.log(" on link from " + this.adornedLink.fromNode + " to " + this.adornedLink.toNode);
console.log(" with shift pressed? " + myDiagram.lastInput.shift);
var spliced = false;
if (myDiagram.lastInput.shift)
{
// work out which of the points on the link was clicked
var link = this.adornedLink;
var numpts = link.pointsCount;
var i;
var x = this.Fp.M; // ##TODO - by inspection in debugger this contains the X coord, but what's documented place to get this?
var y = this.Fp.N; // ##TODO - ditto for Y coord
for (i = 1; !spliced && (i < numpts - 1); i++)
{
if ((link.getPoint(i).x == x) && (link.getPoint(i).y == y))
{
console.log(" .. at point " + i);
// Store off what to do. (This used to be done inline - deferred to after as one of the things
// to try to make it work.)
doSpliceNode = true;
doSpliceIntoLink = link;
doSplicePoint = new go.Point(x, y);
doSpliceIndex = i;
spliced = true;
}
}
}
//if (!doSpliceNode)
{
console.log(".. call base class doMouseDown");
go.LinkReshapingTool.prototype.doMouseDown.call(tool);
}
}
// Override doMouseUp as well. If we had decided during mousedown to do the splice, then stop the tool now.
tool.doMouseUp = function()
{
// First call base class
go.LinkReshapingTool.prototype.doMouseUp.call(tool);
if (doSpliceNode)
{
// Doing splice - stop tool
console.log("STOP TOOL");
this.stopTool();
this.doDeactivate();
}
}
// Finally, override doStop to actually do the splice
tool.doStop = function() {
console.log("doStop");
// First call base class
go.LinkReshapingTool.prototype.doStop.call(tool);
if (doSpliceNode)
{
// now splice the node
console.log("splice node");
spliceNewNodeIntoLink2(doSpliceIntoLink, doSplicePoint, doSpliceIndex); // ##TODO make it respect points in existing link before and after
}
// Reset everything
doSpliceNode = false;
doSpliceIntoLink = null;
doSplicePoint = null;
doSpliceIndex = -1;
}
// Debug variable for inspecting later - not functional
var debugLastLink = null;
// Model, node and links for this application. Based heavily on https://gojs.net/temp/splicing.html and adapted as needed.
var myModel = $(go.GraphLinksModel);
myDiagram.nodeTemplate = $(go.Node,
"Auto",
new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
$(go.Shape, "Circle", { width: 6, height: 6, strokeWidth: 0 }));
myDiagram.linkTemplate =
$(go.Link,
{
relinkableFrom: true, relinkableTo: true,
reshapable: true, resegmentable: true,
/* selectionAdornmentTemplate: ## COMMENT OUT - NOT NEEDED
$(go.Adornment,
$(go.Shape, { isPanelMain: true, stroke: "dodgerblue", strokeWidth: 2 }),
$(go.Shape, "PlusLine",
{
isActionable: true, // so that click works in an Adornment
width: 16, height: 16, stroke: "green", strokeWidth: 4, background: "transparent",
segmentOffset: new go.Point(8, 0),
click: function(e, shape) {
alert(e);
var link = shape.part.adornedPart;
var p0 = link.getPoint(0);
var p1 = link.getPoint(link.pointsCount - 1);
var pt = new go.Point((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
// ##TODO - instead, find the position where the mouse was clicked and place the node there
// ... need to work out which segment of polyline this was in so as to calculate new lines
// ##TODO - handle drag of node so that it just affects segments of lines immediately into it, rather than
// blatting over top of them
// ##TODO - what is object e and its components
spliceNewNodeIntoLink(link, pt);
},
cursor: "pointer"
})
), */
toShortLength: 1
},
new go.Binding("points").makeTwoWay(), // Use the points information from the linkDataArray initializer
$(go.Shape, { strokeWidth: 2 })
);
/* function spliceNewNodeIntoLink(link, pt) { // ## original version no longer called
link.diagram.commit(function(diag) {
var tokey = link.toNode.key;
// add a new node
var newnodedata = { text: "on link", location: go.Point.stringify(pt) };
diag.model.addNodeData(newnodedata);
// and splice it in by changing the existing link to refer to the new node
diag.model.setToKeyForLinkData(link.data, newnodedata.key);
// and by adding a new link from the new node to the original "toNode"
diag.model.addLinkData({ from: newnodedata.key, to: tokey });
// optional: select the new node
diag.select(diag.findNodeForData(newnodedata));
}, "spliced in node on a link");
} */
// Utility function used in one attempt to get this to work. Initializers in nodeDataArray do it via an array of numbers,
// so try that here.
function toArray(nodelist)
{
var returnarray = new Array();
var i;
for (i = 0; i < nodelist.size; i++)
{
var pt = nodelist.elt(i);
returnarray.push(pt.x);
returnarray.push(pt.y);
}
return returnarray;
}
// Function to splice the new node into the link. Parameters are
// - link: the link to splice into
// - pt: the point within the link to turn into a node
// - index: index into existing polyline of that point
function spliceNewNodeIntoLink2(link, pt, index) {
link.diagram.commit(function(diag) {
var oldlinkpointslist = link.points;
var link1pointslist = new go.List(go.Point);
var link2pointslist = new go.List(go.Point);
var i;
// Create new points list, from "from" node to new node to be added
for (i = 0; i <= index; i++)
{
var point = new go.Point(link.getPoint(i).x, link.getPoint(i).y);
link1pointslist.add(point);
}
console.log(link1pointslist);
// .. and from new node to "to" node
for (i = index; i < link.pointsCount; i++)
{
var point = new go.Point(link.getPoint(i).x, link.getPoint(i).y);
link2pointslist.add(point);
}
console.log(link2pointslist);
var tokey = link.toNode.key;
// add a new node
var newnodedata = { text: "on link", location: go.Point.stringify(pt) };
diag.model.addNodeData(newnodedata);
// and splice it in by changing the existing link to refer to the new node
diag.model.setToKeyForLinkData(link.data, newnodedata.key);
// ** NEW CODE
// Code this was based on re-used the existing link, re-purposing it to go from "from" node
// to new node, so do the same, but give it a new points list.
link.points = link1pointslist; // ##TODO find out why this doesn't work
// ... actually it does, but something ditches the points later ...
// so maybe I need to move this code to after the tool has really finished operating
// by saving off the info and calling it in an override of the last tool method that
// gets called (perhaps not - did this and it didn't work)
debugLastLink = link; // ##TEMP
// and by adding a new link from the new node to the original "toNode"
// ** UPDATED to include the second new point list
diag.model.addLinkData({ from: newnodedata.key, to: tokey, points: toArray(link2pointslist) });
// optional: select the new node
diag.select(diag.findNodeForData(newnodedata));
}, "spliced in node on a link");
}
// not called at present
function maySpliceOutNode(node) {
return node.findLinksInto().count === 1 &&
node.findLinksOutOf().count === 1 &&
node.findLinksInto().first() !== node.findLinksOutOf().first();
}
// not called at present
function spliceNodeOutFromLinkChain(node) {
if (maySpliceOutNode(node)) {
node.diagram.commit(function(diag) {
var inlink = node.findLinksInto().first();
var outlink = node.findLinksOutOf().first();
// reconnect the existing incoming link
inlink.toNode = outlink.toNode;
// remove the node and the outgoing link
diag.removeParts([node, outlink], false);
// optional: select the original link
diag.select(inlink);
}, "spliced out node from chain of links");
}
}
// Initialize modeldi
myModel.nodeDataArray = [
{ key: "1" , "location": "30 30" },
{ key: "2" , "location": "130 30" },
{ key: "3" , "location": "30 130" }
];
myModel.linkDataArray = [
{ from: "1", to: "2", "points": [ 30,30, 70,20, 100,40, 130,30 ] },
{ from: "2", to: "3", "points": [ 130,30, 100,80, 70,90, 30,130 ] },
{ from: "3", to: "1", "points": [ 30,130, 20,100, 40,70, 30,30 ] }
];
myDiagram.model = myModel;
</script>
</body>
</html>
Some suggestions:
Call Link.findClosestSegment to find the segment where the user clicked to insert a node.
Don't splice in the new node in an override of Tool.doStop, because that will be called even if the user hit the Escape key to cancel the tool's operation. Do it in either doMouseDown or doMouseUp, depending on the behavior that you want. But doStop is a reasonable time to clean up the tool's state.
I think it should work if you add the new Node and a new Link, connect them together properly, make sure the Node is at the right location, and only then set Link.points explicitly. The TwoWay Binding on Link.points will save the points to the model.
The problem that you are encountering is that when you create a new Node it takes time to be measured and arranged and positioned. Any one of those activities will invalidate the routes of all connected links. And obviously connecting a link with a node will invalidate that link's route. So you have to make sure everything is done in the right order.

How To Add A Blacklist to Location

I am working on something to blacklist unwanted locations with the location service. Here is my current code:
How can I implement a blacklist feature?
if (navigator.geolocation) {
// Locate position
navigator.geolocation.getCurrentPosition(displayPosition, errorFunction);
} else {
alert('Your device location is not approved.');
}
// Success callback function
function displayPosition(pos) {
var mylat = pos.coords.latitude;
var mylong = pos.coords.longitude;
var thediv = document.getElementById('locationinfo');
alert('Your HWID is compliant of ProtoProt regulations.');
}
function errorFunction(pos) {
alert('Error: (PROTOPROT_POS_DENIED). We only use your HWID for checking compliance. Enable location to enter.');
}
Maintain a list of locations given by two points w,y.
Each w,y represent the two opposite points of a w,x,y,z square which represents the blacklisted location.
Each point has longitude and latitude coordinates, so w = [long, lat] and y = [long, lat]
With those, you can rebuild all the [long, lat] of all the w,x,y,z corners of your square, thus representing the blacklisted area.
Now, it's rather easy to know the boundaries of forbidden location: any point [long, lat] which is within the square is blacklisted.
You can store those values within a Javascript Object (a dictionary) which can be stored in a distinct ".js" file. JSON representation could look like:
blacklisted_areas = {
'area 51' : [w, y], // Replace w y with the floats of long and lat
'pink unicorn zoo' : [w, y], // same
// etc.
};
Access:
long = blacklisted_area['area 51'][0]

Fusion Tables layer URL request limit (2048 chars)

I'm using Google Maps to highlight a bunch of countries using Fusion Tables to grab the geometry. You can see an example of this here:
http://jsfiddle.net/4mtyu/689/
var layer = new google.maps.FusionTablesLayer({
query: {
select: locationColumn,
from: tableId,
where: "ISO_2DIGIT IN ('AF','AL','DZ','AD','AO','AG','AR','AM','AU','AT','AZ','BS','BH','BD','BB','BY','BE','BZ','BJ','BT','BO','BA','BW','BR','BN','BG','BF','BI','KH','CM','CA','CV','CF','TD','CL','CN','CO','KM','CG','CD','CR','HR','CU','CY','CZ','DK','DJ','DM','DO','EC','EG','SV','GQ','ER','EE','ET','FJ','FI','FR','GA','GM','GE','DE','GH','GR','GD','GT','GN','GW','GY','HT','HN','HU','IS','IN','ID','CI','IR','IQ','IE','IL')"
},
options : {suppressInfoWindows:true},
styles: [{
polygonOptions: {
fillColor: "#000000",
strokeWeight: "0",
fillOpacity: 0.4
}
}]
});
The problems begin when I try to grab too many items from the table. Google uses a URL with all of the query values to grab the data required and with URL encoding it can grow to be quite large in length.
You can see an example of the URL here if you open up the console and check the URLs being thrown in the errors:
http://jsfiddle.net/4mtyu/690/
The URL it generates in that particular example is 3749 characters, way over the 2048 character limit.
Does anybody have any ideas on a way I could prevent the URL from getting this large but at the same time still be able to select 150+ items?
The easiest solution is to move things client-side:
http://jsfiddle.net/4mtyu/725/
Part 1 :: Initialize the map and fusion tables
You can do this how ever you prefer, just make sure the fusion tables have all countries selected. Example:
function initialize() {
//settings
var myOptions = {
zoom: 2,
center: new google.maps.LatLng(10, 0),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
//get map div
map = new google.maps.Map(document.getElementById('map_div'),
myOptions);
// Initialize padded JSON request
var script = document.createElement('script');
var url = ['https://www.googleapis.com/fusiontables/v1/query?'];
url.push('sql=');
//select all the countries!!
var query = 'SELECT name, kml_4326 FROM ' +
'1foc3xO9DyfSIF6ofvN0kp2bxSfSeKog5FbdWdQ';
var encodedQuery = encodeURIComponent(query);
//generate URL
url.push(encodedQuery);
url.push('&callback=drawMap');//Callback
url.push('&key=AIzaSyAm9yWCV7JPCTHCJut8whOjARd7pwROFDQ');//select all countries
script.src = url.join('');
//Add Script to document
var body = document.getElementsByTagName('body')[0];
body.appendChild(script);
}
Part 2 :: Sort countries and render
(a) Once you have the full list of countries, you have to sort them. A simple indexOf check should do the trick.
(b) After sorting we need turn our countries into LatLon Coordinates, this is done in the constructNewCoordinates function (see below)
(c) Then all that's left is to generate the polygon and add it to our map!
Example:
var countries = [...];
//This is the callback from the above function
function drawMap(data) {
//Get the countries
var rows = data['rows'];
for (var i in rows) {
// (a) //
//If the country matches our filled countries array
if (countries.indexOf(rows[i][0]) !== -1)
var newCoordinates = [];
// (b) //
// Generate geometries and
// Check for multi geometry countries
var geometries = rows[i][1]['geometries'];
if (geometries) {
for (var j in geometries) {
//Calls our render function, returns Polygon Coordinates (see last step);
newCoordinates.push(constructNewCoordinates(geometries[j]));
}
} else {
//Calls our render function, returns Polygon Coordinates (see last step);
newCoordinates = constructNewCoordinates(rows[i][1]['geometry']);
}
// (c) //
//Generate Polygon
var country = new google.maps.Polygon({
paths: newCoordinates,
strokeWeight: 0,
fillColor: '#000000',
fillOpacity: 0.3
});
//add polygon to map
country.setMap(map);
}
}
}
}
Part 3 :: Generating the coordinates
// (b) //
function constructNewCoordinates(polygon) {
var newCoordinates = [];
var coordinates = polygon['coordinates'][0];
for (var i in coordinates) {
newCoordinates.push(
new google.maps.LatLng(coordinates[i][1], coordinates[i][0]));
}
return newCoordinates;
}

Polyline geometry to graphic - SimpleLineSymbol is the only type not displaying on the map

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);
}

How to display markers (huge numbers) on a map which has been logically divided into segments?

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.

Categories