I have a Meteor app where product providers enter their zip code when registering.
This data is stored in users.profile.zipcode.
Flow:
1. Anyone visiting the site can enter a zip code in a search field.
2. A list of product providers with zipcodes within 10 kilometers of that zip code is displayed.
The app will be for Norwegian users to begin with, but will maybe be expanded to different countries in the future.
Can someone provide me with example code of how this can be done, i guess using the Google API or something similar? I'm pretty new to JavaScript so a complete example would be very much appreciated. Hopefully using Meteor.Publish and Meteor.Subscribe, including the display of the data.
Thank you in advance!
First you will have to convert ZIP code to coordinates, there is a zipcodes lib - for US and Canada only, if you're targeted other region/country libs can be easily found on NPM.
For example we have a Meteor Method which accepts form with zipcode field:
import zipcodes from 'zipcodes';
// Create `2dsphere` index
const collection = new Mongo.Collection('someCollectionName');
collection._ensureIndex({zipLoc: '2dsphere'});
Meteor.Methods({
formSubmit(data) {
const zipData = zipcodes.lookup(data.zipcode);
// Save location as geospatial data:
collection.insert({
zipLoc: {
type: "Point",
coordinates: [zipData.longitude, zipData.latitude]
}
});
}
});
To search within radius use next code:
const searchRadius = 10000; // 10km in meters
const zip = 90210;
const zipData = zipcodes.lookup(zip);
collection.find({
zipLoc: {
$near: {
$geometry: [zipData.longitude, zipData.latitude],
$maxDistance: searchRadius
}
}
});
Further reading:
zipcodes NPM library
MongoDB: Geospatial Queries
MongoDB: $near
MongoDB: $geometry
MongoDB: 2dsphere Indexes
Related
Recently I find that I can't download ERA5 land hourly data via Google Earth Engine, and the following code can only return null. But if I replace the first row with "var era51 = ee.ImageCollection('ECMWF/ERA5/DAILY')", it could return the images. Is there something wrong with the ERA5 land hourly data?
Here is the code:
var era51 = ee.ImageCollection("ECMWF/ERA5_LAND/HOURLY")
.filterDate('2018-01-01', '2018-02-02')
.select('total_precipitation');
function exportImageCollection(imgCol) {
var indexList = imgCol.reduceColumns(ee.Reducer.toList(), ["system:index"])
.get("list");
indexList.evaluate(function(indexs) {
for (var i=0; i<indexs.length; i++) {
var image = imgCol.filter(ee.Filter.eq("system:index", indexs[i])).first();
print(image)
}
});
}
exportImageCollection(era51);
EDIT: Turns out that it was a problem on the Dataset side. It is now fixed. Happy downloading :)
Same problem here, about last week my script worked smoothly, but today it just fails. I use to work with the python API, but I've been able to download Landsat-[5,8] images with no problem.
I tried to use Earth Engine Javascript API to download the same area with both: an URL (image.GetDownloadURL()) and to Drive (Export.image.toDrive()); but both approaches also failed.
Tests in Javascript API:
var imgcol = ee.ImageCollection("ECMWF/ERA5_LAND/HOURLY");
var subset = imgcol.filterDate("2010-09-11T10", "2010-09-11T11").filterBounds(geometry);
var img = subset.map(function(x){return x.clip(geometry);}).first();
Map.addLayer(subset.select("surface_latent_heat_flux"))
var url = img.getDownloadURL(
{
name: 'single_band',
bands: ['surface_latent_heat_flux'],
region: geometry
}
);
print(url); //url is printed but fails in the download
Export.image.toDrive(
{
image: img,
description: 'LET',
folder: 'ee_test',
region: geometry,
scale: 9000
});
Could it be an error in Earth Engine end?
EDIT: Turns out that it was a problem on the Dataset side. It is now fixed. Happy downloading :)
I'm creating a solution that converts a revit model to IFC file format using Autodesk Forge - Model Derivative API. This API hands me a JSON file with the hierarchy of the converted model, and a JSON file with all separate objects and their properties.
After converting the model I need to analyze specific properties from parts of the model. But not all information I need is stored in objects' properties. I also need to use XYZ coordinates of objects to get real results, but I believe the model derivative API doesn't generate XYZ data.
I've already searched all the properties of the objects to see if they contain any kind of data about their location in comparison to other objects, but they don't contain that information. I've searched for other ways to extract geometry/coordinates from Revit, but haven't found a real solution.
https://forge.autodesk.com/en/docs/model-derivative/v2/tutorials/extract-metadata-from-source-file/
In step 5 of this tutorial you can see the data that I have (the properties of each object).
There is no way to get the XYZ data from the Model Derivative API the way that you are hoping.
I'd also say that if you are looking to convert to IFC, there is already a conversion service for that in the Model Derivative API. But in case you really need a custom file format, here is how you could get XYZ, below.
There are two other options though that you can consider.
One, is to use the Design Automation for Revit API. You would be able to make an Addin that pulls the needed data from the headless Revit environment.
Another option is to launch a headless Forge Viewer and get the XYZ data of the model from there.
The headless viewer is a tutorial in the Viewer API documentation that you can check out. Here is the code from it (v6) for reference.
var viewerApp;
var options = {
env: 'AutodeskProduction',
accessToken: ''
};
var documentId = 'urn:<YOUR_URN_ID>';
Autodesk.Viewing.Initializer(options, onInitialized);
function onInitialized() {
viewerApp = new Autodesk.Viewing.ViewingApplication('MyViewerDiv');
viewerApp.registerViewer(viewerApp.k3D, Autodesk.Viewing.Viewer3D);
viewerApp.loadDocument(documentId, onDocumentLoaded);
}
function onDocumentLoaded(lmvDoc) {
var modelNodes = viewerApp.bubble.search(av.BubbleNode.MODEL_NODE); // 3D designs
var sheetNodes = viewerApp.bubble.search(av.BubbleNode.SHEET_NODE); // 2D designs
var allNodes = modelNodes.concat(sheetNodes);
if (allNodes.length) {
viewerApp.selectItem(allNodes[0].data);
if (allNodes.length === 1){
alert('This tutorial works best with documents with more than one viewable!');
}
} else {
alert('There are no viewables for the provided URN!');
}
}
Once you're accessing the viewer, here is some code that you can get the bounding box of an element or elements by dbIds that I've used successfully.
/**
* Uses dbId element fragments to build boundingbox of element
* #param {Array<number>} dbIds dbIds of element to find boundingBox
* #return {THREE.Box3} dbId elements bounding box
*/
getBoundingBox(dbIds) {
const totalBox = new THREE.Box3();
dbIds.forEach((dbId) => {
const fragBox = new THREE.Box3();
const fragIds = [];
const instanceTree = viewer3D.model.getInstanceTree();
instanceTree.enumNodeFragments(dbId, function(fragId) {
fragIds.push(fragId);
});
const fragList = viewer3D.model.getFragmentList();
fragIds.forEach(function(fragId) {
fragList.getWorldBounds(fragId, fragBox);
totalBox.union(fragBox);
});
});
return totalBox;
}
From this BoundingBox which is a THREE.Box3 object, you can get some XYZ information about the elements. Also, there is code here using the 'fragments' that will allow you to get different element geometry more specifically if that is more useful for the XYZ you need to define.
I want an using firebase real time database to store events of some sort.
An event can have a changing status in its life cycle.
I want to be able to fetch all active events near a user current location.
Because it doesn't seems like geofire enables me to save the location on the event document, I would have to get all events near the location, and then filter by status and given event ids (is that event possible??).
Is there a better way?
Thanks!
Geofire works by returning what's close to coordinates you give it. For an idea or starter template, check out https://getaa.org. (click the map icon top center and enter 'san francisco' or 'baton rouge' if you are in an area with no meetings.
The code is all on github.
Here's a code snippet:
function loadQuery(lat,lng,today){
//console.log('loadQuery function fired with ' + lat,lng,today);
var geoKeys = [];
var records=[];
var geoQuery = geofire.query({
center: [lat,lng],
radius: radius
});
var onKeyEnteredRegistration = geoQuery.on("key_entered", function(key, location, distance) {
geoKeys.push(key);
//console.log('geoQuery event KEY ENTERED with key: ' + key);
});
// GeoKeys now in array
var onReadyRegistration = geoQuery.on("ready", function() {
//console.log('geoQuery event READY');
if(geoKeys.length > 0){
toastUp('Fetching Meetings...');
// Get recordset for each key into sites array
readFirebaseNodes(geoKeys).then(function(value) {
//filter for today
var todaysMeetings = dayFilter(today);
drop(todaysMeetings);
}, function(err) {
//console.log(err); // Error!
});
} else {
toastUp('No area meetings found. You are encouraged to volunteer to add them. Click Meetings Manager to become a site administrator.');
toastDown(2000);
}
});
}
The Firebase Realtime Database can only query on a single property. The fact that GeoFire can filter on two axes (longitude and latitude) is already quite magical (thanks to the use of geohashes). Adding one more value to the mix is beyond what GeoFire is made for.
But it's actually quite simple to only show nearby active events: keep a location/reference in your database with just the locations of those active events. Then you can simply create a GeoFire object on that location, and it will only find nearby active events:
// Create a Firebase reference where GeoFire will store its information
var firebaseRef = firebase.database().ref("activeEventLocations");
// Create a GeoFire index
var geoFire = new GeoFire(firebaseRef);
This question already has an answer here:
Query for nearby locations
(1 answer)
Closed 5 years ago.
I have a table called users in firebase, the table looks like this:
The structure of this table is:
users
|_
uid1
|_
latitude: ...
longitude: ...
name: ...
uid2
|_
latitude: ...
longitude: ...
name: ...
I want to write a Cloud Function called getUserByLocation, the sample request url would be https://someurl/getUserByLocation?latitude=40.45654&longitude=-86.68468, which takes a pair of latitude and longitude as an original point, then go to the users table to find all users within 10 miles then return them back. It doesn't seem very hard and in fact, I have something that works:
Calculate latLower, latUpper, longLower and longUpper and make a circle like this:
Then use query ...orderByChild('latitude').startAt(latLower).endAt(latUpper).once('value')... to select users whose latitudes are in range, I cannot consider longitude at this point because apparently I can only order the results by one key
I check all results selected from step 2, if the longitude is in range too (between longLower and longUpper), then this user should be returned
This approach works for sure, however, I am thinking, what if there are millions of users? This approach is just not efficient. As a matter of fact, I wrote a script to create more than 1 million users and then used this approach to select users, usually, it'll take more than 10 secs to see the results and this is not acceptable.
My question is, is there any way I can improve the search efficiency in this situation? I've googled ElasticSearch but currently have no knowledge of what that is, is that something that can be used, in this case, to improve search?
You can do this using GeoFire but you'll have to store your coordinates in a separate root node to the users. Something like user-location, where each child is a user key and location managed by GeoFire. You use it to more efficiently query a radius at a location and retrieve the IDs of the users within the bounds – however, then you will have to get the user data for each user ID found.
const geofire = require('geofire');
const geoFire = new geofire(admin.database().ref('user-location'));
const geoQuery = geoFire.query({ center, radius });
let keyListener = null;
let keyLocations = {};
return new Promise((res, rej) => {
geoQuery.on('ready', function () {
console.log(keyLocations);
keyListener.cancel();
geoQuery.cancel();
res();
});
keyListener = geoQuery.on('key_entered', (key, coordinates, distance) => {
keyLocations[key] = { coordinates, distance };
});
});
I have very long web pages paths reported to google analytics:
/#/legends_g01/games/legends_g01_02_academy_i-170909-55/notes/1/dynamics
/#/legends_02_academy_i/games/legends_g01_02_academy_i-170912-64/notes/12/players
/#/legends_05/games/legends_05-170912-84/notes/22/players
/#/legends_g01_02_academy_i/games/legends_g01_02_academy_i-170919-78/notes/34/levels
I'm using Core API to create a query where I need to have a metric ga:users with dimension by the last path part (7th). The starting part of the path doesn't matter here and should be ignored.
So if there is ga:pagePathLevel7 then I can use
dimension: ga:pagePathLevel7
metrics: ga:users
And see the result like this:
dynamics: 34
players: 45
levels: 87
How can I do this without ga:pagePathLevel7?
It seems that I'm the only one here with such a problem.
As I failed to find a direct solution I ended up adding custom dimensions to my google analytics. I added the dimensions for the last important path parts and changed the code on the site to supply this data together with the pageView url.
import ReactGA from 'react-ga';
export const statDimension = (dimensionName, value) => {
if(value)
{
let obj = {};
obj[dimensionName] = value;
ReactGA.set(obj);
}
};
export const statPageView = (url, game_id, clip_num) => {
if(!url)
{
url = window.location.hash;
}
//set game_id
statDimension(STAT_DIM_GAME_ID, game_id);
//set clip number
statDimension(STAT_DIM_CLIP_NUM, clip_num);
ReactGA.pageview(url);
return null;
};
I use react-ga npm module for submitting data to google analytics.
Now I can use custom dimensions together with filters on my urls to get stats based on the parts of the path with depth > 4.
May be that's not an elegant solution but is a working one.
Hope this will be helpful for somebody like me.