I am currently using a Google Maps Fusion Table for County information to display the county boundaries for Texas. There are over 200+ counties in Texas. I am looping through an array of values for each county and need to color-code the county based on the value in the array. There are 4 levels of colors for the county: Stop, Warning, Watch and Open. Everything seems to be working, except that the color is only being applied to 5 counties. The limit of styles is 5 and the limit of layers is also 5, but I am only using 1 layer and 4 styles.
Can someone tell me what I am dong wrong? Or is this just not possible via the API?
Below is a snippet of the code:
var styles = new Array();
var ftLayer = new google.maps.FusionTablesLayer();
function loadTexas() {
loadFusionLayer('TX');
ftLayer.setMap(map);
for (var i = 0; i < aryCounty.length; i++) {
styleLayer("'State Abbr.' = 'TX' AND 'County Name' = '" +
aryCounty[i].County + "'", 1000);
}
ftLayer.set('styles', styles);
}
function loadFusionLayer(state) {
if (state != null) {
where = "'State Abbr.' IN ('" + state + "')";
}
var select = "geometry, 'State Abbr.', 'County Name'";
ftLayer.setOptions({
query: {
select: select,
from: countyTableId,
where: where
}
});
}
function styleLayer(where, actualValue) {
var color = setPolygonColorBy(actualValue);
styles.push({
where: where,
polygonOptions: {
fillColor: color,
fillOpacity: 0.6
}
});
}
function setPolygonColorBy(actualValue, divisor) {
var status;
var stop = STATUS_LAYER_STYLES["Stop"].color;
var warning = STATUS_LAYER_STYLES["Warning"].color;
var watch = STATUS_LAYER_STYLES["Watch"].color;
var open = STATUS_LAYER_STYLES["Open"].color;
if (actualValue >= minValue && actualValue < midValue) {
status = watch;
}
else if (actualValue >=midValue && actualValue < maxValue) {
status = warning;
}
else if (actualValue >= maxValue) {
status = stop;
}
else {
status = open;
}
return status;
}
You really only have 4 styles. You need to get the dollar value for each county into your own Fusion Table. You could download the US Counties Fusion Table, perhaps only the TX counties and create a new FT. Then add you own dollar value column. (A simpler better approach would be to merge your actualValues with the Counties table, but I'm not famiiar with merging table. You need your actual Values and the State-County key values. The merge should create a new table owned by you)
Then you can create your 4 styles as described in the Maps FT Docs.
There may be syntax errors here.
ftLayer = new google.maps.FusionTablesLayer({
query: {
select: 'geometry',
from: countyTable_with_actualValues_Id },
styles: [{
// default color
where: "'State Abbr.' ='" + state + "'";
polygonOptions: {
fillColor: open
}
}, {
where: "'State Abbr.' ='" + state + "' AND actualValue >= " + maxValue;
polygonOptions: {
fillColor: stop
}
}, {
where: "'State Abbr.' ='" + state + "' AND actualValue >= " + minValue + "AND actualValue < " + midValue;
polygonOptions: {
fillColor: watch
},
// Final condition left as an exercise :-)
}]
});
ftLayer.setMap(map);
Answer Update:
If your company does not want to push the data out and you are writing an internal application that is only available to employees, then you don't want to use Fusion Tables. The data in Fusion Tables is generally available to the public and is published one of two ways:
Follow-up To Your May 25, 2012 Comment (if you decide to move forward with FusionTables):
With your comments, I understand better what you are doing. I believe the code you have in the loadTexas function that is calling the styleLayer function to create the FusionTablesStyle objects is correct.
The part that looks suspicious is the ordering of the code in the loadTexas function and the loadFusionLayer function. There is also a missing var where declaration in the loadFusionLayer function. I don't think it is causing a problem (at least not in the code you have shown), but it does inadvertently create a global, so corrected that problem in the code that follows. I suggest making the following changes:
Create a new var fusionTablesOptions in the global space and use fusionTablesOptions to set up all of the options for the ftLayer FusionTablesLayer.
Change the declaration of var ftLayer so that it is assigned to: null.
Iterate the aryCounty array, build the styles array, and assign it to the fusionTablesOptions.styles property.
Assign fusionTablesOptions.map to the existing instance of google.maps.Map named: map (and remove this line of code in the loadTexas function: ftLayer.setMap(map);).
Assign fusionTablesOptions.query to the object that is built in the loadFusionLayer function.
Now that all of the necessary fusionTablesOptions properties have been set, create a new instance of google.maps.FusionTablesLayer, passing fusionTablesOptions to the constructor function.
var styles = new Array();
//Step 1:
var fusionTablesOptions = {};
//Step 2:
var ftLayer = null;
function loadTexas() {
//Step 3:
for (var i = 0; i < aryCounty.length; i++) {
//Call to the styleLayer function not shown to reduce code size
}
fusionTablesOptions.styles = styles;
//Step 4:
fusionTablesOptions.map = map;
loadFusionLayer('TX');
}
function loadFusionLayer(state) {
var where = null; //Corrects the missing var keyword
if (state != null) {
where = "'State Abbr.' IN ('" + state + "')";
}
var select = "geometry, 'State Abbr.', 'County Name'";
//Step 5:
fusionTablesOptions.query : {
select: select,
from: countyTableId,
where: where
}
//Step 6:
ftLayer = new google.maps.FusionTablesLayer( fusionTablesOptions );
}
Apologies for the large answer, but I wanted to make sure I conveyed all of my suggestions clearly in a way thought would be easy to review or implement.
Related
I am trying to connect my Alexa skill to an Amazon RDS mySQL database using node.js in AWS Lambda. I tested the connection before uploading it to lambda and it worked but when I upload it I get a 'process exited before completing request' or a 'There was a problem with the skills response' error.
'use strict';
const Alexa = require('alexa-sdk');
const APP_ID = 'amzn1.ask.skill.11069fc0-53bc-4cd0-8961-dd41e2d812f8';
var testSQL = 'SELECT weight, height from users where pin=1100';
//=========================================================================================================================================
//Database connection settings
//=========================================================================================================================================
var mysql = require('mysql');
var config = require('./config.json');
// Add connection details for dB
var pool = mysql.createPool({
host : config.dbhost,
user : config.dbuser,
password : config.dbpassword,
database : config.dbname
});
// var dbHeight, dbWeight, dbMuscle, dbExerciseOne, dbExerciseTwo, dbExerciseThree, dbExerciseFour;
var dbResult;
function searchDB(quest) {
pool.getConnection(function(err, connection) {
// Use the connection
console.log(quest);
connection.query(quest, function (error, results, fields) {
// And done with the connection.
connection.release();
// Handle error after the release.
if (!!error) {
console.log('error')
}
else {
console.log(results[0]);
dbResult = results[0];
return dbResult;
console.log(dbResult.height);
}
process.exit();
});
});
};
//searchDB(testSQL);
//=========================================================================================================================================
//TODO: The items below this comment need your attention.
//=========================================================================================================================================
const SKILL_NAME = 'My Application';
const GET_FACT_MESSAGE = "Here's your fact: ";
const HELP_MESSAGE = 'You can say tell me a space fact, or, you can say exit... What can I help you with?';
const HELP_REPROMPT = 'What can I help you with?';
const STOP_MESSAGE = 'Goodbye!';
var name, pinNumber;
//=========================================================================================================================================
//Editing anything below this line might break your skill.
//=========================================================================================================================================
const handlers = {
'LaunchRequest': function () {
if(Object.keys(this.attributes).length === 0){
this.attributes.userInfo = {
'userName': '',
'pinNo': 0
}
this.emit('GetPinIntent');
}
else{
name = this.attributes.userInfo.userName;
pinNumber = this.attributes.userInfo.pinNo;
var sql = "";
//var result = searchDB(sql);
//var uWeight = result.weight;
//var uHeight = result.height;
var speechOutput = 'Welcome ' + name + 'Please select an option: Check My BMI, Create Exercise Plan, Create Meal Plan, Update Height and Weight, Update workout status?';
this.emit(':ask', speechOutput);
}
},
'GetPinIntent': function (){
this.emit(':ask','Welcome to my Application, as this is your first time please say your name followed by your pin. For example, my name is Jason and my pin is zero one zero one');
//this.emit(':responseReady');
},
'RememberNameID': function (){
var filledSlots = delegateSlotCollection.call(this);
this.attributes.userInfo.userName = this.event.request.intent.slots.name.value;
this.attributes.userInfo.pinNo = this.event.request.intent.slots.pin.value;
var speechOutput = 'Welcome ' + this.attributes.userInfo.userName + ' we have stored your Pin Number and we will call you by name next time. Please select an option: BMI or exercise';
this.response.speak(speechOutput);
this.emit(':responseReady');
},
'CheckBMI': function(){
var sql = 'SELECT height, weight FROM users WHERE pin=' + this.attributes.userInfo.pinNo;
var heightWeight = searchDB(sql);
dbHeight = parseInt(heightWeight.height);
dbWeight = parseInt(heightWeight.weight);
var speechOutput = bmiCalculator(dbHeight, dbWeight);
this.emit(':ask', speechOutput);
},
'AMAZON.HelpIntent': function () {
const speechOutput = HELP_MESSAGE;
const reprompt = HELP_REPROMPT;
this.response.speak(speechOutput).listen(reprompt);
this.emit(':responseReady');
},
'AMAZON.CancelIntent': function () {
this.response.speak(STOP_MESSAGE);
this.emit(':responseReady');
},
'AMAZON.StopIntent': function () {
this.response.speak(STOP_MESSAGE);
this.emit(':responseReady');
},
'SessionEndedRequest': function() {
console.log('session ended!');
this.emit(':saveState', true);
}
};
exports.handler = function (event, context, callback) {
var alexa = Alexa.handler(event, context, callback);
alexa.APP_ID = APP_ID;
alexa.dynamoDBTableName = 'fitnessDB';
alexa.registerHandlers(handlers);
alexa.execute();
};
function delegateSlotCollection(){
console.log("in delegateSlotCollection");
console.log("current dialogState: "+this.event.request.dialogState);
if (this.event.request.dialogState === "STARTED") {
console.log("in Beginning");
var updatedIntent=this.event.request.intent;
//optionally pre-fill slots: update the intent object with slot values for which
//you have defaults, then return Dialog.Delegate with this updated intent
// in the updatedIntent property
this.emit(":delegate", updatedIntent);
} else if (this.event.request.dialogState !== "COMPLETED") {
console.log("in not completed");
// return a Dialog.Delegate directive with no updatedIntent property.
this.emit(":delegate");
} else {
console.log("in completed");
console.log("returning: "+ JSON.stringify(this.event.request.intent));
// Dialog is now complete and all required slots should be filled,
// so call your normal intent handler.
return this.event.request.intent;
}
};
function bmiCalculator (userHeight, userWeight ) {
var speechOutput = " ";
var h = userHeight/100;
var calcBMI = 0;
calcBMI = userWeight / (h*h);
calcBMI = calcBMI.toFixed(2);
if (calcBMI < 18.5) {
speechOutput += "Based on your weight of " +weight+ " kilograms and your height of " + height + " metres, your BMI is " +calcBMI+ ". Meaning you are currently underweight.";
speechOutput += " I would advise you to increase your calorie intake, whilst remaining active.";
return speechOutput;
}
else if (calcBMI >=18.5 && calcBMI < 25){
speechOutput += "Based on your weight of " +weight+ " kilograms and your height of" + height + " metres, your BMI is " +calcBMI+ ". Meaning you are currently at a normal weight.";
speechOutput += " I would advise you to stay as you are but ensure you keep a healthy diet and lifestyle to avoid falling above or below this.";
this.response.speak(speechOutput);
return speechOutput;
}
else if (calcBMI >=25 && calcBMI < 29.9){
speechOutput += "Based on your weight of " +weight+ " kilograms and your height of" + height + " metres, your BMI is " +calcBMI+ ". Meaning you are currently overweight.";
speechOutput += " I would advise you to exercise more to fall below this range. A healthy BMI is ranged between 18.5 and 24.9";
this.response.speak(speechOutput);
return speechOutput;
}
else{
speechOutput += "Based on your weight of " +weight+ " kilograms and your height of" + height + " metres, your BMI is " +calcBMI+ ". Meaning you are currently obese.";
speechOutput += " I would advise you to reduce your calorie intake, eat more healthy and exercise more. A healthy BMI is ranged between 18.5 and 24.9";
return speechOutput;
}
};
The code outlines my database connection. I created the connection query as a function as I will need to make varying queries to the database based on the context. Is there a way to create a function within the exports.handler function that will only call the query when needed?
Or are there any other solutions with regards to connecting to the database in such a way.
You are running into multiple issues, without using a Promise or await, your call is running async and you will never get an answer immediately from the RDS for the lambda call. you need to create a function that will wait for an answer before continuing its logic.
The other issue you will run into is the MySQL RDS instance is it running constantly there may be a cold start issue.
The last thing is in the AWS lambda console be sure to allocate enough resources in compute and time to run this function the default 128 mb of memory and the time to run the function can be adjusted to improve performance
Use sync-mysql to make synchronous queries to a mysql database. It worked for me.
I am using express and npm mysql (Link)
I want to do a call using
query('SELECT * FROM TABLE WHERE ?', where, cb)
where is a javascript object f.e. : {col1: 'one', col2: 'two'}
But it seems that this doesn't work. It works for SET though to update multiple columns at once.
I want a general method where I can send a different combination of columns to search. I was wondering if there is a built in method to do this.
Meanwhile, I created this script:
var andQuery = "";
var andParams = [];
var isFirst = true;
for (var filter in where) {
if (where.hasOwnProperty(filter)) {
if(!isFirst) {
andQuery += " AND ? ";
} else {
andQuery += " ? ";
isFirst = false;
}
andParams.push({[filter]: where[filter]});
}
}
db.query(
'SELECT * FROM `Table` WHERE ' + andQuery, andParams, (err, results) => {
Update #2: Okay, I'm pretty sure my error in update #1 was because of indexing out of bounds over the array (I'm still not used to JS indexing at 0). But here is the new problem... if I write out the different combinations of the loop manually, setting the page index to 1 in moveItem() like so:
newForm.moveItem(itemsArray[0][0], 1);
newForm.moveItem(itemsArray[0][1], 1);
newForm.moveItem(itemsArray[0][2], 1);
newForm.moveItem(itemsArray[1][0], 1);
newForm.moveItem(itemsArray[1][1], 1);
newForm.moveItem(itemsArray[1][2], 1);
newForm.moveItem(itemsArray[2][0], 1);
...
...I don't get any errors but the items end up on different pages! What is going on?
Update #1:: Using Sandy Good's answer as well as a script I found at this WordPress blog, I have managed to get closer to what I needed. I believe Sandy Good misinterpreted what I wanted to do because I wasn't specific enough in my question.
I would like to:
Get all items from a page (section header, images, question etc)
Put them into an array
Do this for all pages, adding these arrays to an array (i.e: [[all items from page 1][all items from page 2][all items from page 3]...])
Shuffle the elements of this array
Repopulate a new form with each element of this array. In this way, page order will be randomized.
My JavaScript skills are poor (this is the first time I've used it). There is a step that produces null entries and I don't know why... I had to remove them manually. I am not able to complete step 5 as I get the following error:
Cannot convert Item,Item,Item to (class).
"Item,Item,Item" is the array element containing all the items from a particular page. So it seems that I can't add three items to a page at a time? Or is something else going on here?
Here is my code:
function shuffleForms() {
var itemsArray,shuffleQuestionsInNewForm,fncGetQuestionID,
newFormFile,newForm,newID,shuffle, sections;
// Copy template form by ID, set a new name
newFormFile = DriveApp.getFileById('1prfcl-RhaD4gn0b2oP4sbcKaRcZT5XoCAQCbLm1PR7I')
.makeCopy();
newFormFile.setName('AAAAA_Shuffled_Form');
// Get ID of new form and open it
newID = newFormFile.getId();
newForm = FormApp.openById(newID);
// Initialize array to put IDs in
itemsArray = [];
function getPageItems(thisPageNum) {
Logger.log("Getting items for page number: " + thisPageNum );
var thisPageItems = []; // Used for result
var thisPageBreakIndex = getPageItem(thisPageNum).getIndex();
Logger.log( "This is index num : " + thisPageBreakIndex );
// Get all items from page
var allItems = newForm.getItems();
thisPageItems.push(allItems[thisPageBreakIndex]);
Logger.log( "Added pagebreak item: " + allItems[thisPageBreakIndex].getIndex() );
for( var i = thisPageBreakIndex+1; ( i < allItems.length ) && ( allItems[i].getType() != FormApp.ItemType.PAGE_BREAK ); ++i ) {
thisPageItems.push(allItems[i]);
Logger.log( "Added non-pagebreak item: " + allItems[i].getIndex() );
}
return thisPageItems;
}
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
Logger.log('shuffle ran')
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
function shuffleAndMove() {
// Get page items for all pages into an array
for(i = 2; i <= 5; i++) {
itemsArray[i] = getPageItems(i);
}
// Removes null values from array
itemsArray = itemsArray.filter(function(x){return x});
// Shuffle page items
itemsArray = shuffle(itemsArray);
// Move page items to the new form
for(i = 2; i <= 5; ++i) {
newForm.moveItem(itemsArray[i], i);
}
}
shuffleAndMove();
}
Original post: I have used Google forms to create a questionnaire. For my purposes, each question needs to be on a separate page but I need the pages to be randomized. A quick Google search shows this feature has not been added yet.
I see that the Form class in the Google apps script has a number of methods that alter/give access to various properties of Google Forms. Since I do not know Javascript and am not too familiar with Google apps/API I would like to know if what I am trying to do is even possible before diving in and figuring it all out.
If it is possible, I would appreciate any insight on what methods would be relevant for this task just to give me some direction to get started.
Based on comments from Sandy Good and two SE questions found here and here, this is the code I have so far:
// Script to shuffle question in a Google Form when the questions are in separate sections
function shuffleFormSections() {
getQuestionID();
createNewShuffledForm();
}
// Get question IDs
function getQuestionID() {
var form = FormApp.getActiveForm();
var items = form.getItems();
arrayID = [];
for (var i in items) {
arrayID[i] = items[i].getId();
}
// Logger.log(arrayID);
return(arrayID);
}
// Shuffle function
function shuffle(a) {
var j, x, i;
for (i = a.length; i; i--) {
j = Math.floor(Math.random() * i);
x = a[i - 1];
a[i - 1] = a[j];
a[j] = x;
}
}
// Shuffle IDs and create new form with new question order
function createNewShuffledForm() {
shuffle(arrayID);
// Logger.log(arrayID);
var newForm = FormApp.create('Shuffled Form');
for (var i in arrayID) {
arrayID[i].getItemsbyId();
}
}
Try this. There's a few "constants" to be set at the top of the function, check the comments. Form file copying and opening borrowed from Sandy Good's answer, thanks!
// This is the function to run, all the others here are helper functions
// You'll need to set your source file id and your destination file name in the
// constants at the top of this function here.
// It appears that the "Title" page does not count as a page, so you don't need
// to include it in the PAGES_AT_BEGINNING_TO_NOT_SHUFFLE count.
function shuffleFormPages() {
// UPDATE THESE CONSTANTS AS NEEDED
var PAGES_AT_BEGINNING_TO_NOT_SHUFFLE = 2; // preserve X intro pages; shuffle everything after page X
var SOURCE_FILE_ID = 'YOUR_SOURCE_FILE_ID_HERE';
var DESTINATION_FILE_NAME = 'YOUR_DESTINATION_FILE_NAME_HERE';
// Copy template form by ID, set a new name
var newFormFile = DriveApp.getFileById(SOURCE_FILE_ID).makeCopy();
newFormFile.setName(DESTINATION_FILE_NAME);
// Open the duplicated form file as a form
var newForm = FormApp.openById(newFormFile.getId());
var pages = extractPages(newForm);
shuffleEndOfPages(pages, PAGES_AT_BEGINNING_TO_NOT_SHUFFLE);
var shuffledFormItems = flatten(pages);
setFormItems(newForm, shuffledFormItems);
}
// Builds an array of "page" arrays. Each page array starts with a page break
// and continues until the next page break.
function extractPages(form) {
var formItems = form.getItems();
var currentPage = [];
var allPages = [];
formItems.forEach(function(item) {
if (item.getType() == FormApp.ItemType.PAGE_BREAK && currentPage.length > 0) {
// found a page break (and it isn't the first one)
allPages.push(currentPage); // push what we've built for this page onto the output array
currentPage = [item]; // reset the current page to just this most recent item
} else {
currentPage.push(item);
}
});
// We've got the last page dangling, so add it
allPages.push(currentPage);
return allPages;
};
// startIndex is the array index to start shuffling from. E.g. to start
// shuffling on page 5, startIndex should be 4. startIndex could also be thought
// of as the number of pages to keep unshuffled.
// This function has no return value, it just mutates pages
function shuffleEndOfPages(pages, startIndex) {
var currentIndex = pages.length;
// While there remain elements to shuffle...
while (currentIndex > startIndex) {
// Pick an element between startIndex and currentIndex (inclusive)
var randomIndex = Math.floor(Math.random() * (currentIndex - startIndex)) + startIndex;
currentIndex -= 1;
// And swap it with the current element.
var temporaryValue = pages[currentIndex];
pages[currentIndex] = pages[randomIndex];
pages[randomIndex] = temporaryValue;
}
};
// Sourced from elsewhere on SO:
// https://stackoverflow.com/a/15030117/4280232
function flatten(array) {
return array.reduce(
function (flattenedArray, toFlatten) {
return flattenedArray.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten);
},
[]
);
};
// No safety checks around items being the same as the form length or whatever.
// This mutates form.
function setFormItems(form, items) {
items.forEach(function(item, index) {
form.moveItem(item, index);
});
};
I tested this code. It created a new Form, and then shuffled the questions in the new Form. It excludes page breaks, images and section headers. You need to provide a source file ID for the original template Form. This function has 3 inner sub-functions. The inner functions are at the top, and they are called at the bottom of the outer function. The arrayOfIDs variable does not need to be returned or passed to another function because it is available in the outer scope.
function shuffleFormSections() {
var arrayOfIDs,shuffleQuestionsInNewForm,fncGetQuestionID,
newFormFile,newForm,newID,items,shuffle;
newFormFile = DriveApp.getFileById('Put the source file ID here')
.makeCopy();
newFormFile.setName('AAAAA_Shuffled_Form');
newID = newFormFile.getId();
newForm = FormApp.openById(newID);
arrayOfIDs = [];
fncGetQuestionID = function() {
var i,L,thisID,thisItem,thisType;
items = newForm.getItems();
L = items.length;
for (i=0;i<L;i++) {
thisItem = items[i];
thisType = thisItem.getType();
if (thisType === FormApp.ItemType.PAGE_BREAK ||
thisType === FormApp.ItemType.SECTION_HEADER ||
thisType === FormApp.ItemType.IMAGE) {
continue;
}
thisID = thisItem.getId();
arrayOfIDs.push(thisID);
}
Logger.log('arrayOfIDs: ' + arrayOfIDs);
//the array arrayOfIDs does not need to be returned since it is available
//in the outermost scope
}// End of fncGetQuestionID function
shuffle = function() {// Shuffle function
var j, x, i;
Logger.log('shuffle ran')
for (i = arrayOfIDs.length; i; i--) {
j = Math.floor(Math.random() * i);
Logger.log('j: ' + j)
x = arrayOfIDs[i - 1];
Logger.log('x: ' + x)
arrayOfIDs[i - 1] = arrayOfIDs[j];
arrayOfIDs[j] = x;
}
Logger.log('arrayOfIDs: ' + arrayOfIDs)
}
shuffleQuestionsInNewForm = function() {
var i,L,thisID,thisItem,thisQuestion,questionType;
L = arrayOfIDs.length;
for (i=0;i<L;i++) {
thisID = arrayOfIDs[i];
Logger.log('thisID: ' + thisID)
thisItem = newForm.getItemById(thisID);
newForm.moveItem(thisItem, i)
}
}
fncGetQuestionID();//Get all the question ID's and put them into an array
shuffle();
shuffleQuestionsInNewForm();
}
I am trying to do room highlighting in forge viewer.
In revit I have created lines that represent the borders of a room. After conversion to svf I know the dbids of those lines. Now I want to know the start and stop points (vertices) of those lines so that I can create a Three.Shape() of the room borders.
[EDIT] I get the fragId from dbId
function getFragIdFromDbId(viewer, dbid){
var returnValue;
var it = viewer.model.getData().instanceTree;
it.enumNodeFragments(dbid, function(fragId) {
console.log("dbId: " + dbid + " FragId : " + fragId);
returnValue = fragId;
}, false);
return returnValue;
}
Question:
Once I know the fragId is there a way to see its start and stop points(vertices)? Also will those vertices be world space or local space?
This is what I ended up doing. Note make sure the model is finished loading before calling instanceTree. Also in my case the dbid and fragid where one to one, not sure if this will always be the case in the instance tree.
function getFragIdFromDbId(viewer, dbid) {
var returnValue;
var it = viewer.model.getData().instanceTree;
it.enumNodeFragments(dbid, function (fragId) {
console.log("dbId: " + dbid + " FragId : " + fragId);
returnValue = fragId;
}, false);
return returnValue;
}
...
// only need the start vertex
var floatArray = [];
for (var i = 0; i < dbidArray.length; i++) {
var fragId = getFragIdFromDbId(viewer, dbidArray[i]);
var mesh = viewer.impl.getRenderProxy(viewer.model, fragId);
var matrixWorld = mesh.matrixWorld;
var lmvBufferGeometry = mesh.geometry;
var lmvFloatArray = lmvBufferGeometry.vb; //this will have an array of 6 values 0,1,2 are start vertext , 3,4,5 are end vertex
floatArray.push(lmvFloatArray[0]);
floatArray.push(lmvFloatArray[1]);
floatArray.push(lmvFloatArray[2]);
}
//use matrixWorld to convert array to worldSpace
Given two locations you can calculate a route in Google Maps.
Is it possible to find all zip codes along the route?
Given a zip code, can I expand the area easily with a 10 km radius and find all zip codes in that area?
What methods should I use to get this information? Tutorials are welcome. I don't need a complete working solution, although if one is available that would be really nice.
You need a data source containing the zipcode (ZCTA) polygons. One possible source is this FusionTable.
proof of concept
proof of concept showing ZCTA polygons
Note: since it queries for the zip code at every point along the route, it will take longer to finish the longer the route is.
code that performs the query (using the Google Visualization API):
function queryForZip(latlng) {
//set the query using the current latlng
var queryStr = "SELECT geometry, ZIP, latitude, longitude FROM "+ tableid + " WHERE ST_INTERSECTS(geometry, CIRCLE(LATLNG"+latlng+",1))";
var queryText = encodeURIComponent(queryStr);
var query = new google.visualization.Query('http://www.google.com/fusiontables/gvizdata?tq=' + queryText);
//set the callback function
query.send(addZipCode);
}
function addZipCode(response) {
if (!response) {
alert('no response');
return;
}
if (response.isError()) {
document.getElementById('status').innerHTML += 'Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage()+"<br>";
return;
}
FTresponse = response;
//for more information on the response object, see the documentation
//http://code.google.com/apis/visualization/documentation/reference.html#QueryResponse
numRows = response.getDataTable().getNumberOfRows();
numCols = response.getDataTable().getNumberOfColumns();
for(i = 0; i < numRows; i++) {
var zip = response.getDataTable().getValue(i, 1);
var zipStr = zip.toString()
if (!zipcodes[zipStr]) {
zipcodes[zipStr] = zipStr;
document.getElementById('zipcodes').innerHTML += zipStr+"<br>";
}
}
}