JSON string changes its keys for Google MAPS API v3 - json

I've seen this question a few times already on here but the responses aren't quite applicable to my case.
I am using a google.maps.Polygon object to save bounds that is drawn on a map.
Later I am using the "getPath" method and I add this to a Json string.
The JSON string is always different. Sometimes I can grab the polygon corners by:
thisShape.data.b[i].kb
thisShape.data.b[i].lb
other times by
thisShape.data.b[i].Qa
thisShape.data.b[i].Ra
etc.
It seems over time, the key for the Json string changes. However I am not responsible what the Google Api returns as far the Polygon object goes.
Is there a way to grab the longtitute and latitude without knowing the key?
Any ideas how to loop through my polygons without fear that it'll break in a few weeks?
Many thanks
Suppose you have a method called "SavePolygonData" that, among other things, creates a Json string like this:
var allData = {};
$("div.shapes").each(function(){
var id = $(this).attr("id");
var type = id.split('_')[0];
var feature = MapToolbar.features[type+'Tab'][id];
var vertices = feature.getPath();
allData[id] = { color: feature.fillColor, data: vertices};
});
var allDataJson = JSON.stringify(allData);
and then later another method that reads the json string somewhere else like this:
for (var polyId in allData) {
if (allData.hasOwnProperty(polyId)) {
var thisShape = allData[polyId];
var color = thisShape.color;
var vertices=new Array();
for ( var i=0; i<thisShape.data.b.length; ++i )
{
var coordX = thisShape.data.b[i].kb; //Problem here
var coordY = thisShape.data.b[i].lb; //Problem here
var point = new google.maps.LatLng(coordX, coordY);
vertices[i] = point;
}
var poly = new google.maps.Polygon({
strokeWeight: 2,
fillColor: color,
paths: vertices
});
poly.setMap(map);
}
I can't use the getLat() methods on the json string because obviously it only contains strings. Instead I have to create a new data structure for my "data" variable that contains processed data instead of feature.getPath().
That's what I need help with. What is the structure of that new data structure?
I have noticed there is the google.maps.geometry.encoding.encodePath() method.
But I need to be able to decode it in server side.

The problem in your SavePolygonData example code is that you are trying to use the vertices data directly in your JSON object. Instead, you need to use the documented methods to get the latitude and longitude values from the vertex array and serialize those.
To fix it, you can change this code:
var vertices = feature.getPath();
allData[id] = { color: feature.fillColor, data: vertices};
to:
var vertices = getLatLngVertices( feature.getPath() );
allData[id] = { color: feature.fillColor, data: vertices };
and elsewhere define this function:
function getLatLngVertices( path ) {
var pathArray = path.getArray();
var vertices = [];
for( var i = 0, n = pathArray.length; i < n; i++ ) {
var latLng = pathArray[i];
vertices.push({ lat: latLng.lat(), lng: latLng.lng() });
}
return vertices;
}
Now in your code that parses this JSON you can use:
var data = thisShape.data;
for( var i = 0, n = data.length; i < n; ++i ) {
var point = data[i];
vertices[i] = new google.maps.LatLng( point.lat, point.lng );
}

It looks like getPath() returns an MVCArray of LatLng, from which you are supposed to extract latitude and longitude by calling the lat() and lng() methods, not by directly accessing properties. If it were possible in JavaScript, these properties would have been declared private. But the same applies to the MVCArray, for which you have the choice of looping over with forEach(), or, if are not at ease with lambda functions yet, with getAt(), using the result of getLength() as guard:
polygon.getPath().forEach(function(latLng, idx) {
var lat = latLng.lat();
var lng = latLng.lng();
// do something
});
or
var path = polygon.getPath();
var len = path.getLength();
for (var idx = 0; idx < len; idx++) {
var latLng = path.getAt(idx);
var lat = latLng.lat();
var lng = latLng.lng();
// do something
}
These examples are OK if the polygon consists of only one path. If not, you will have to loop over paths too.

Related

How to smooth mesh triangles in STL loaded BufferGeometry

I´m trying to load some STL files using Three.js. The models are loaded correctly, but there are too many triangles that I would like to merge/smooth.
I had successfully applied smooth loading terrains in other 3D formats, but I can´t do it with the BufferGeometry that results from loading an STL file with the STLLoader.
_
var material = new THREE.MeshLambertMaterial( { ... } );
var path = "./models/budah.stl";
var loader = new THREE.STLLoader();
loader.load( path, function ( object ) {
object.computeBoundingBox();
object.computeBoundingSphere();
object.computeFaceNormals();
object.computeVertexNormals();
object.normalizeNormals();
object.center();
// Apply smooth
var modifier = new THREE.SubdivisionModifier( 1);
var smooth = smooth = object.clone();
smooth.mergeVertices();
smooth.computeFaceNormals();
smooth.computeVertexNormals();
modifier.modify( smooth );
scene.add( smooth );
});
This is what I tried, it throws an error: Uncaught TypeError: smooth.mergeVertices is not a function
If I comment the "mergeVertices()" line, what I get is a different error: Uncaught TypeError: Cannot read property 'length' of undefined in SubdivisionsModifier, line 156.
It seems that the sample codes I´m trying are outdated (this is happenning a lot recently due to the massive changes in the Three.JS library). Or maybe I´m forgetting something. The fact is that the vertices seems to be null..?
Thanks in advance!
It seems I was looking in the wrong direction: smoothing the triangles has nothing to do with the SubdivisionsModifier... What I needed was easier than that, just compute the vertex BEFORE applying the material, so it can use SmoothShading instead of FlatShading (did I got it right?).
The problem here was that the BufferGeometry returned by the STLLoader has not calculated vertices/vertex, so I had to do it manually. After that, apply mergeVertices() just before computeVertexNormals() and voilà! The triangles dissappear and everything is smooth:
var material = new THREE.MeshLambertMaterial( { ... } );
var path = "./models/budah.stl";
var loader = new THREE.STLLoader();
loader.load( path, function ( object ) {
object.computeBoundingBox();
object.computeVertexNormals();
object.center();
///////////////////////////////////////////////////////////////
var attrib = object.getAttribute('position');
if(attrib === undefined) {
throw new Error('a given BufferGeometry object must have a position attribute.');
}
var positions = attrib.array;
var vertices = [];
for(var i = 0, n = positions.length; i < n; i += 3) {
var x = positions[i];
var y = positions[i + 1];
var z = positions[i + 2];
vertices.push(new THREE.Vector3(x, y, z));
}
var faces = [];
for(var i = 0, n = vertices.length; i < n; i += 3) {
faces.push(new THREE.Face3(i, i + 1, i + 2));
}
var geometry = new THREE.Geometry();
geometry.vertices = vertices;
geometry.faces = faces;
geometry.computeFaceNormals();
geometry.mergeVertices()
geometry.computeVertexNormals();
///////////////////////////////////////////////////////////////
var mesh = new THREE.Mesh(geometry, material);
scene.add( mesh );
});
Than, you can convert it back to BufferGeometry, because it's more GPU/CPU efficient for more complex models:
var geometry = new THREE.Geometry();
geometry.vertices = vertices;
geometry.faces = faces;
geometry.computeFaceNormals();
geometry.mergeVertices();
geometry.computeVertexNormals();
var buffer_g = new THREE.BufferGeometry();
buffer_g.fromGeometry(geometry);
var mesh = new THREE.Mesh(buffer_g, material);
scene.add( mesh )
Happened this issue for me while loading an obj file. If you have a 3d software like 3dsmax:
Open the obj file,
Go to polygons selection mode and select all polygons.
Under the Surface properties panel, click 'Auto Smooth' button.
Export the model back to obj format
Now you won't have to call the functions geometry.mergeVertices() and geometry.computeVertexNormals();. Just load the obj and add to the scene, mesh will be smooth.
EDIT:
My obj files had meshphongmaterial by default and on changing the shading property to value 2 the mesh became smooth.
child.material.shading = 2
STL does not support vertex index.
That is reason it has duplicated vertex of all triangles.
Each vertex has its normal as triangle normal.
As a result, at same position( multiple very closed vertices), there is multiple normal value.
This leads to non-smooth surface of geometry when using Normal for lighting calculation.

Why does (myMC.myArray = myOtherMC.myOtherArray;) cause any changes to myMC.myArray to also change myOtherMC.myOtherArray?

var myArray:Array = new Array();
var myMC:MovieClip = new MovieClip();
myMC.myArray = myArray;
trace(myMC.myArray[10]); //Output: undefined
var newMC:MovieClip = new MovieClip();
newMC.myOtherArray = myMC.myArray;
newMC.myOtherArray[10] = [];
newMC.myOtherArray[10][0] = 100;
trace(myMC.myArray[10]); //Output: 100
Why does that happen, and is there any way to avoid it?
EDIT:
Found a function that can clone associative arrays here.
Here is the function (from the above link):
function clone(source:Object):*
{
var myBA:ByteArray = new ByteArray();
myBA.writeObject(source);
myBA.position = 0;
return(myBA.readObject());
}
Does making the function return type "*" mean that it can be any type? or is it something specific to objects/arrays?
It's because you have passed a reference of the original array to the new clip. Arrays, as objects, are not primitives and therefore will always be referenced.
If you would like to clone an array, keeping the original intact, use this method:
var b:Array = a.concat();
b will be a new array. a can modified without changing b.

speed up update of a heat map

I have several relatively big sets of data (around 86.000 points of latitude and longitude) that shows the global temperature distribution for different years. I represent this data with a heat map and for that I use the google map library. My goal is to show/animate the global temperature change over a range of X-years. Thus, for each selected year, I need to change/update a new heat map that shows the global temperature distribution of that year. I managed to write a code that updates the heat map by scrolling over a range bar or by pressing a forward/backward button. The problem I am facing is, that the update takes quite long (around 3-4 seconds). So, my question is, is there any way to speed up the update?
This is what I did to update the heat map:
First, my data is an array (length 86.000), where each element has the following values: latitude,longitude,temperature of year x1, temperature of year x2, ..., temperature of year xn.
So, the latitude and longitude coordinates are fixed for every year.
This is the code where I initialise my heat map:
var map, heatmap, heatmapData;
function initialize() {
heatmapData = new google.maps.MVCArray();
var mapOptions = {
zoom: 2,
center: new google.maps.LatLng(5,45),
mapTypeId: google.maps.MapTypeId.TERRAIN
};
map = new google.maps.Map(document.getElementById('map_canvas'),
mapOptions);
//Take data of 1st year
for (var i = 1; i < data.length; i+=1) {
var latLng = new google.maps.LatLng(data[i][2], data[i][1]);
var temperature = data[i][3]
var weightedLoc = {
location: latLng,
weight:temperature
};
heatmapData.push(weightedLoc);
};
heatmap = new google.maps.visualization.HeatmapLayer({
data: heatmapData,
dissipating: false,
map: map,
opacity: 0.8,
radius: 1.2
});
heatmap.setMap(map);
}
And my update function looks like this:
function update(year){
var index = year-firstYear+3;
//just change value of temperature
heatmapData.forEach(function(elm, i){
elm.weight=data[i+1][index];
});
heatmap.set('data',heatmapData);
}
How could I improve this code in order to get a faster performance?
Thx in advance!
Most of the time consumed by rendering the Heatmap is actually on the google maps heatmap functionallity, and there is little you can do there, unless you want to build your own heatmap funcionallity.
You could improve little bit your code by speeding up the update function using a simple for loop instead using foreach.
//just change value of temperature
heatmapData.forEach(function(elm, i){
elm.weight=data[i+1][index];
});
//you could use instead:
for(var i=0, len=heatmapData.length; i< len; i++) {
heatmapData[i].weight = data[i+1][index];
}
Take a look here to compare performance of different loop options: http://jsperf.com/for-vs-foreach/37
As you already have the DATA in memory, replacing that one big array with few arrays and disposing the original data array won't be a big memory issue.
var heatbyyear = [];
for (var j=3 ; j < yearsOfData; j++) {
heatbyyear.push([]);
}
for (var i = 1; i < data.length; i+=1) {
//Take data of 1st year
var thisyear = [];
var latLng = new google.maps.LatLng(data[i][2], data[i][1]);
var temperature = data[i][3]
var weightedLoc = {
location: latLng,
weight:temperature
};
heatmapData.push(weightedLoc);
for (var j=3 ; j < yearsOfData; j++) {
heatbyyear[j].push(data[i][j]);
}
};
data = null;
and then you simply call:
function update(year){
var index = year-firstYear+3;
heatmap.set('data',heatbyyear[index]);
}

Google 3D Earth How to find the center point from all the markers and set center view

I am new to Google 3D Earth API
I would like to add multiple palcemarkers on Google Earth?
This is my sample code can any one suggest me where I am going to wrong to add multiple markers?
var ge;
var placemaker = new Array();
var point = new Array();
google.load("earth", "1");
function init() {
google.earth.createInstance('map3d', initCB, failureCB);
}
function initCB(instance) {
ge = instance;
ge.getWindow().setVisibility(true);
ge.getNavigationControl().setVisibility(ge.VISIBILITY_HIDE);
for(var i = 0; i < data.length; i++ )
{
// Set the placemark's location.
point[i] = ge.createPoint( data[i].content );
point[i].setLatitude( data[i].lat );
point[i].setLongitude( data[i].log );
placemark[i].setGeometry(point[i]);
// Add the placemark to Earth.
ge.getFeatures().appendChild(placemark[i]);
}
}
function failureCB(errorCode) {}
google.setOnLoadCallback(init);
Here data is an array of object(s) has lat, log and content.
I will not seen any of the placemarker on the earth.
If i push one single placemarker will working fine but not working if i go with loop.
In Google Map V there is options for bounds.
is there any options available for 3D earth?
You need to create a Placemark object using createPlacemark().
You probably don't need to create an array of Placemarks since the google earth API keeps the list of placemarks from which you can iterate using ge.getFeatures().getChildNodes().
for(var i = 0; i < data.length; i++ )
{
// Create the Placemark
var placemark = ge.createPlacemark('');
// Set the placemark's location.
point[i] = ge.createPoint('');
point[i].setLatitude( data[i].lat );
point[i].setLongitude( data[i].lon );
placemark.setGeometry(point[i]);
// Add the placemark to Earth.
ge.getFeatures().appendChild(placemark);
}
Also, probably won't need array of points since have the array of data.
Can simplify this to the following:
for(var i = 0; i < data.length; i++ )
{
// Create the Placemark
var placemark = ge.createPlacemark('');
// Set the placemark's location.
var point = ge.createPoint('');
point.setLatitude( data[i].lat );
point.setLongitude( data[i].lon );
placemark.setGeometry(point);
// Add the placemark to Earth.
ge.getFeatures().appendChild(placemark);
}
If you want to compute the center bounds of a bunch of generic KML features you can use an Google Earth API extension: https://code.google.com/p/earth-api-utility-library/wiki/GEarthExtensionsDomReference#computeBounds(object)
But if you just have an array of points then you can easily compute the center manually then set the LookAt to the computed center view point.
The final initCB() would look like this:
function initCB(instance) {
ge = instance;
ge.getWindow().setVisibility(true);
ge.getNavigationControl().setVisibility(ge.VISIBILITY_HIDE);
var latSum = 0.0;
var lonSum = 0.0;
for(var i = 0; i < data.length; i++ )
{
// Set the placemark's location.
var point = ge.createPoint('');
point.setLatitude( data[i].lat );
point.setLongitude( data[i].lon );
latSum += data[i].lat;
lonSum += data[i].lon;
var placemark = ge.createPlacemark(data[i].content);
placemark.setGeometry(point);
// Add the placemark to Earth.
ge.getFeatures().appendChild(placemark);
}
// Create LookAt at the center view point
var lookAt = ge.createLookAt('');
lookAt.set(latSum / data.length, lonSum / data.length, 0,
ge.ALTITUDE_RELATIVE_TO_GROUND, 0, 0, 20000);
ge.getView().setAbstractView(lookAt);
}

Maps google-apps-script info using distances along a route/path

I am trying to figure out a way to show items using the map script with the following info:
10 miles north running along US 1 plot a marker 10 feet to the right(east) of US 1. And to set for example that mile 0 starts at the intersection between US 1 and Main Street.
Perhaps someone has run into this before or something similar and will be kind enough to give me some pointers.
Thanks!
Following Eric's tip I was able to create a function to grab a polyline for the road centerline from Google Maps. Then use the google api service to convert that polyline into latitude and longitude coordinates. From then on, it was all spherical geometry coding. Below is my humble little code:
//Function to grab a centerline from the Map Api
//start = lat and long for beginning of roadway centerline ie [27.64681, -82.38438]
//end = lat and long for end of roadway centerline ie [27.71248, -82.33518]
//startmile = beginning milepost
function grabmap(start, end, startmile){
startmile = parseFloat(startmile);
var points = [];
var plinex = [];
var pliney = [];
var directions = Maps.newDirectionFinder().setOrigin(start).setDestination(end).getDirections();
// Much of this code is based on the template referenced in
// http://googleappsdeveloper.blogspot.com/2010/06/automatically-generate-maps-and.html
for (var i in directions.routes) {
for (var j in directions.routes[i].legs) {
for (var k in directions.routes[i].legs[j].steps) {
// Parse out the current step in the directions
var step = directions.routes[i].legs[j].steps[k];
// Call Maps.decodePolyline() to decode the polyline for
// this step into an array of latitudes and longitudes
var path = Maps.decodePolyline(step.polyline.points);
points = points.concat(path);
}
}
}
var lengthvector = (points.length / 2);
for ( i = 0; i <= points.length; i++){
if ( i % 2 == 0){
plinex = plinex.concat(points[i]);
pliney = pliney.concat(points[(i+1)]);
}
}
var plineVector = new Array(plinex.length - 2);
for ( i = 0; i <= plinex.length - 2; i++){
plineVector[i] = new Array(2);
plineVector[i][0] = plinex[i];
plineVector[i][1] = pliney[i];
}
return plineVector;
}
I haven't used the Maps Service extensively, but it should be possible. You use the DiretionFinder to get the latitudes and longitudes of points along the path, and then do some math to get the offset for the marker.