I have a page with a mapbox map, and I want the map to remember a saved position when reloading the page. Apparently the string format from getCenter is not the correct lng/lat format that I can use to center map. How do I get it in the right format?
I use map.getZoom and map.getCenter to save some variables into local.storage
First I set the variables, and a starting position if no value is set:
var mzoom = localStorage.getItem("mzoomKey");
if(mzoom == null){
var mzoom = 13;
}
var posi = localStorage.getItem("posiKey");
if(posi == null){
var posi = [9.84861, 55.86128];
}
and then these variables is used for zoom and center of the mapbox map:
center: posi, // starting position [lng, lat]
zoom: mzoom, // starting zoom
and then I set the local.storage variables when the user clicks a button:
var mzoom = map.getZoom();
localStorage.setItem("mzoomKey", mzoom);
var posi = map.getCenter();
localStorage.setItem("posiKey", posi);
console.log(posi);
The zoom Part works - but the center part dosent.
the Console log writes:
ko {lng: 9.84861, lat: 55.86128}
lng_lat.js:151 Uncaught Error: `LngLatLike` argument must be specified as a LngLat instance, an object {lng: <lng>, lat: <lat>}, an object {lon: <lng>, lat: <lat>}, or an array of [<lng>, <lat>]
at ko.convert (lng_lat.js:151:15)
at Map.jumpTo (camera.js:1005:32)
at new Map (map.js:575:18)
at map_V2.html:61:13
How do I get the center position in the right format?
Related
I'm using svg.js and svgdom on Node to generate SVG files. Here's my code:
// set up svg.js and svgdom
const { createSVGWindow } = require('svgdom');
const window = createSVGWindow();
const document = window.document;
const { SVG, registerWindow } = require('#svgdotjs/svg.js');
registerWindow(window, document);
const draw = SVG(document.element).size(100, 100);
// generate SVG text and title
var title = draw.element('title').words('noun');
var text = draw.text('text').add(title);
var width = text.length();
That last line throws TypeError: cannot read property 'style' of null in svgdom/main-require.cjs:3721:38.
This works when I do it in the browser; something about moving to Node and svgdom causes it to fail. Is there another way I can calculate the width of this text element, or a different way to attach the title to the text? (The title is being used to show additional information on hover.) Calling text.width() always returns 0.
I'm using svg.js 3.0.16 and svgdom 0.1.8. Should I open an issue on one of them? If so, which one?
Basically the problem is that after you rotate the camera, the points that are given as arguments in the callback for dragging are not what I expected. I'm guessing I have to Rotate the given points also but I just couldn't.
Can Someone explain what is going on, is this some kind of bug or what should I do in order the sprite to follow the mouse cursor?
Easiest way to explain the problem is to reproduce it:
1) Go to Phaser Example Runner
2) Copy- Paste this code:
var config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
scene: {
preload: preload,
create: create
}
};
var game = new Phaser.Game(config);
function preload ()
{
this.load.image('eye', 'assets/pics/lance-overdose-loader-eye.png');
}
function create ()
{
var image = this.add.sprite(200, 300, 'eye').setInteractive();
this.cameras.main.setRotation(Math.PI);
image.on('pointerover', function () {
this.setTint(0x00ff00);
});
image.on('pointerout', function () {
this.clearTint();
});
this.input.setDraggable(image);
this.input.on('dragstart', function (pointer, gameObject) {
gameObject.setTint(0xff0000);
});
this.input.on('drag', function (pointer, gameObject, dragX, dragY) {
console.log(`x: ${dragX}, y: ${dragY}`);
gameObject.x = dragX;
gameObject.y = dragY;
});
this.input.on('dragend', function (pointer, gameObject) {
gameObject.clearTint();
});
}
3) Open the console, drag around the Eye and look at what coordinates are given.
4) If you remove line 24 (the rotation of the camera) Everything works as expected.
(The example is taken from Phaser 3 Official examples and a bit changed for the bug)
According to Phaser's API Documentation on the setRotation() method, the rotation given in radians applies to everything rendered by the camera. Unfortunately, your pointer isn't rendered by the camera so it doesn't get the same rotated coordinates. Not sure if this is a bug with the library or just a poorly documented exception, but I believe there is a workaround.
Create 2 variables to hold an initial position and a final position:
var image = this.add.sprite(200, 300, 'eye').setInteractive();
var initial = [];
var final = [];
Populate the initial position in your .on('dragstart') method:
this.input.on('dragstart', function (pointer, gameObject) {
initial = [
gameObject.x,
gameObject.y,
pointer.x,
pointer.y
];
gameObject.setTint(0xff0000);
});
Then, populate the final variable in your .on('drag') method:
this.input.on('drag', function (pointer, gameObject, dragX, dragY) {
final = [
gameObject.x, // not necessary but keeping for variable shape consistency
gameObject.y, // not necessary but keeping for variable shape consistency
pointer.x,
pointer.y
];
gameObject.x = initial[0] + (initial[2] - final[2]);
gameObject.y = initial[1] + (initial[3] - final[3]);
});
All we're doing here is keeping track of the change in pointer position and mimicking that change in our gameObject.
I figured an issue, while i have thousands of pins over the map, i am using drawing tool to draw shapes free hand and then executing the Intersection on "drawingEnded" event, While i could see the intersection should return more than it actually returns,
Am i missing something ? For Example, If there are around 500 pins under the new area drawn, Intersection method only returns 100 or few more,
My Spider Cluster Configuration:
` Microsoft.Maps.loadModule(['SpiderClusterManager'], function () {
spiderManager = new SpiderClusterManager(map, pinssame, {
//clusteredPinCallback: function (cluster) {
// //Customize clustered pushpin.
// cluster.setOptions({
// color: 'red',
// icon:'https://www.bingmapsportal.com/Content/images/poi_custom.png'
// });
//},
pinSelected: function (pin, cluster) {
if (cluster) {
showInfobox(cluster.getLocation(), pin);
} else {
showInfobox(pin.getLocation(), pin);
}
},
pinUnselected: function () {
hideInfobox();
},
gridSize: 80
});
});
`
Intersection Function Code which gets triggered after "drawingEnded" event:
` function findIntersectingData(searchArea) {
//Ensure that the search area is a valid polygon, should have 4 Locations in it's ring as it automatically closes.
if (searchArea && searchArea.getLocations().length >= 4) {
//Get all the pushpins from the pinLayer.
//var pins = spiderManager._data;
//Using spatial math find all pushpins that intersect with the drawn search area.
//The returned data is a copy of the intersecting data and not a reference to the original shapes,
//so making edits to them will not cause any updates on the map.
var intersectingPins = Microsoft.Maps.SpatialMath.Geometry.intersection(pins, searchArea);
//The data returned by the intersection function can be null, a single shape, or an array of shapes.
if (intersectingPins) {
//For ease of usem wrap individudal shapes in an array.
if (intersectingPins && !(intersectingPins instanceof Array)) {
intersectingPins = [intersectingPins];
}
var selectedPins = [];
//Loop through and map the intersecting pushpins back to their original pushpins by comparing their coordinates.
for (var j = 0; j < intersectingPins.length; j++) {
for (var i = 0; i < pins.length; i++) {
if (Microsoft.Maps.Location.areEqual(pins[i].getLocation(), intersectingPins[j].getLocation())) {
selectedPins.push(pins[i]);
break;
}
}
}
//Return the pushpins that were selected.
console.log(selectedPins);
return selectedPins;
}
}
return null;
}
`
The function is not returning accurate pin data,
Am i missing something here ?
Any Help Appreciated,
Thanks & Regards,
Shohil Sethia
UPDATE :
Just figured, It is an assumption ,I have multiple pins with same coordinates over the layer, Is this the reason that it returns only pins which intersects with different coordinates over the map ?,
Thanks & Regards,
Shohil Sethia
The method returns objects that represent the intersection, not the exact copies of input shapes. So yes, if multiple pushpins with the same coordinates are within the area, only one pushpin of that coordinates will be in the result, since that alone is good enough as a representation.
You can try the sample below, only one pushpin is returned:
// Creates a polygon of current map bounds
var polygon = new Microsoft.Maps.SpatialMath.locationRectToPolygon(map.getBounds());
// Creates a bunch of the pushpins of the same coordinates(map center)
var pushpin1 = new Microsoft.Maps.Pushpin(map.getCenter());
var pushpin2 = new Microsoft.Maps.Pushpin(map.getCenter());
var pushpin3 = new Microsoft.Maps.Pushpin(map.getCenter());
var pushpin4 = new Microsoft.Maps.Pushpin(map.getCenter());
var pushpin5 = new Microsoft.Maps.Pushpin(map.getCenter());
// Adds the shapes to map for some visualization
map.entities.push([polygon, pushpin1, pushpin2, pushpin3, pushpin4, pushpin5]);
// Only one pushpin is returned as result
var intersectingPin = Microsoft.Maps.SpatialMath.Geometry.intersection([pushpin1, pushpin2, pushpin3, pushpin4, pushpin5], polygon);
Have you checked if the number of results adds up when taking duplicate pins into account?
I got a solution, Since Intersection API ignore multiple pushPins with same coordinates, Therefore there is another API named as contains which takes two parameters which are the shape and the pushpin, and it returns whether it is contained in that shape or not in a boolean form. So true if pushpin is in that shape, and false in the other way.
function findIntersectingData(searchArea) {
//Ensure that the search area is a valid polygon, should have 4 Locations in it's ring as it automatically closes.
if (searchArea && searchArea.getLocations().length >= 4) {
var selectedPins = [];
for (var i = 0; i < pins.length; i++) {
if (Microsoft.Maps.SpatialMath.Geometry.contains(searchArea, pins[i])) {
selectedPins.push(pins[i]);
}
}
//Return the pushpins that were selected.
console.log(selectedPins);
//return updatePrescriberTerr(selectedPins);
return selectedPins;
}
return null;
}
Therefore in the above function the we can loop it from the pushPins array and form the intersection set accordingly based on the boolean values.
Hope it helps to those with similar scenario !
Regards,
Shohil Sethia
I have a javascript array of objects where each object has .ui attribute that contains an existing SVG shape/element of various kinds ( a self contained "g" element with others inside ).
I want to arrange these objects as a force graph layout in D3. So, instead of having d3 create circles (or whatever) I want to assign one of my ui objects to the graph.
I have created a simplified fiddle << HERE >> for that. The objects that are pre-existing and should be part of the graph are the 3 colored rectangles. In this example I failed to achieve that. The d3 append command can be used just to construct shapes/elements, but not to append an already constructed one. I was trying to do this, and it cannot work in D3:
node.append( function(d) { d.ui.node(); }) ...
... as mentioned, this wont work as d3 does not append elements.
var svgContainer = d3.select("#svgContainer");
// =============================================================
// creates the SVG elements to be used ( 3 colored rectangles )
// =============================================================
var element0a = svgContainer.append("g").attr("transform","translate(100,100)");
var element0b = element0a.append("rect").attr("x",0).attr("y",0).attr("width",20).attr("height",10).attr("fill","red");
var element1a = svgContainer.append("g").attr("transform","translate(100,200)");
var element1b = element1a.append("rect").attr("x",0).attr("y",0).attr("width",20).attr("height",10).attr("fill","green");
var element2a = svgContainer.append("g").attr("transform","translate(100,300)");
var element2b = element2a.append("rect").attr("x",0).attr("y",0).attr("width",20).attr("height",10).attr("fill","blue");
// =============================================================
// fills up an object array that contains details plus the UI attribute
// =============================================================
var nodeArray = new Array();
nodeArray[0] = { id : "000", label : "label 000", ui : element0a };
nodeArray[1] = { id : "001", label : "label 001", ui : element1a };
nodeArray[2] = { id : "002", label : "label 002", ui : element2a };
// not interested in dealing with the edge/link right now, just empty
var linkArray = new Array();
// =============================================================
// D3 force layout stuff
// =============================================================
var force = self.force = d3.layout.force()
.nodes(nodeArray)
.links(linkArray)
.gravity(.05)
.distance(80)
.charge(-100)
.size([600, 600])
.start();
var node = svgContainer.selectAll("g.node")
.data(nodeArray)
.enter().append("svg:g")
.attr("class", "node")
.call(force.drag);
// HERE (below) COMES THE ISSUE, where you see append("cicle") I want to append the nodeArray's ".ui" element.
node.append("circle") // <---
.attr("class", "node")
.attr("r",10)
.attr("fill","orange")
.call(force.drag);
force.on("tick", function() {
node.attr("transform", function(d) {return "translate(" + d.x + "," + d.y + ")";});
});
Based on advice from Lars Kotfoff :
Working fiddle here
If the elements to be graph-ed already exist, simply skip the enter() D3 phase and use select() or selectAll() based on a common characteristic of your elements ( a class name for instance ).
This is how the element got created ( added a specific class for allowing an isolated selection ):
var element0a = svgContainer.append("g").attr("class","node").attr("transform","translate(100,100)");
var element0b = element0a.append("rect").attr("x",0).attr("y",0).attr("width",20).attr("height",10).attr("fill","red");
This is thene part of the code replacing the enter() phase
var node = svgContainer.selectAll("g.node")
.data(nodeArray)
.call(force.drag);
This is what was removed
var node = svgContainer.selectAll("g.node")
.data(nodeArray)
.enter().append("svg:g")
.attr("class", "node")
.call(force.drag);
// HERE (below) COMES THE ISSUE, where you see append("cicle") I want to append the nodeArray's ".ui" element.
node.append("circle") // <---
.attr("class", "node")
.attr("r",10)
.attr("fill","orange")
.call(force.drag);
The problem is that you append the elements to svgContainer to create them, thus they are already attached to something.
What I would suggest as a workaround is to store the element properties in a json file and then read the configuration from the json.
There might be something more elegant.
i have an xml file with coordinates in it and i want to draw a line of those points on an Openlayers Map. I already have a Openlayers.Layer.Vector and i am creating a feature as follows:
var points = [];
for (var i = 0; i < coords.length; i++)
{
point = new OpenLayers.Geometry.Point(aPointsArray[i].lon, aPointsArray[i].lat);
points.push(point);
}
var geometry = new OpenLayers.Geometry.LineString(points);
var feature = new OpenLayers.Feature.Vector(geometry, null,
{
strokeColor: aColor,
strokeOpacity: 0.7,
strokeWidth: 3
});
aLayer.addFeatures([feature]);
This works a expected and i am seeing a line on my map. The problem is now, that the points i get from the .xml are changed dynamically by another program and I want to draw those changes live on my map. I already have a method that updates the maps periodically but how do i update the feature/geometry to the new points ?
Try aLayer.drawFeature(yourChangedFeature);