I have multiple firebase items that I am looping through and they have name, categoryId, lat, lon.
I am trying to calculate a distance from 2 lat,lon (one is the one in firebase), the other is the user's location.
All of this is fine, and I can calculate it fine too. However, how do I inject/map the new variable into the firebase array that I am subscribed to?
this.categoryId = this.navParams.get('categoryId');
afoDatabase.list('/list', {query: {
orderByChild: "categoryId",
equalTo: parseInt(this.categoryId)
}}).subscribe(listItems => {
this.items = listItems; //need to add distance within array
loadingPopup.dismiss().catch(() => {});
});
For example, I have a variable called distance, that calculates everything and has the value. How do I add it in the this?items - so I can call it from the HTML side?
Thanks for the help :)
To inject, first create a function and create a variable (e.g. this.distance) and get the value of your function on this variable. After that use a for loop to increment, based on length - and use this.items[i]["distance"] to add to each one. For example:
this.categoryId = this.navParams.get('categoryId');
afoDatabase.list('/list', {query: {
orderByChild: "categoryId",
equalTo: parseInt(this.categoryId)
}}).subscribe(listItems => {
this.items = listItems;
this.geolocation.getCurrentPosition({timeout:15000}).then((resp) => {
this.myLat = resp.coords.latitude;
this.myLong = resp.coords.longitude;
}).catch((error) => {
console.log('Error getting location', error);
});
for (var i = 0, len = this.items.length; i < len; i++) {
this.distance = this.calculateDistance(this.myLat, this.myLong, this.items[i].lat, this.items[i].lng);
this.items[i]["distance"] = Math.round(this.distance);
console.log('testing myLat', this.myLat)
console.log('testing myLong', this.myLong)
}
console.log('testing inject', this.items)
loadingPopup.dismiss().catch(() => {});
});
Function
calculateDistance(lat1:number,lon1:number,lat2:number,lon2:number){
let R = 6371; // km
let dLat = (lat2 - lat1) * Math.PI / 180;
let dLon = (lon2 - lon1) * Math.PI / 180;
let a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
let c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
let d = R * c;
return d;
}
Related
I'm working with react-three-fiber and struggling with Line2 and some memory issues. I left my application running for a time and then when I came back my computer was frozen... After I recovered I realized that the the application was gradually consuming more and more memory and sometimes, but not always, showing this error: "webgl warning: drawelementsinstanced: instance fetch requires 30, but attribs only supply 20". I tried changing the getPoints divisions and the number of points in the line itself and I was able to make things a little bit better, but it still didn't solve the issue. The problem seems to be comming from the 'getPoints' method, but I'm not quite so sure. Can someone help me?
export function SingleTrace({
particleSystem,
physics,
steps,
detail,
width,
color,
}) {
const line = useRef();
let positions = useMemo(() => [], [physics]);
useFrame(() => {
let stepsSoFar = positions.length;
if (stepsSoFar === 0) {
let randomX = ((Math.random() - 1 / 2) * particleSystem.boundary.w +
particleSystem.boundary.x) as number;
let randomY = ((Math.random() - 1 / 2) * particleSystem.boundary.h +
particleSystem.boundary.y) as number;
let randomZ = ((Math.random() - 1 / 2) * particleSystem.boundary.d +
particleSystem.boundary.z) as number;
positions = [vec(randomX, randomY, randomZ)]; //otherwise just use a random position
} else if (stepsSoFar > steps) {
positions.shift();
}
let lastPosition = positions[positions.length - 1];
let totalField = vec();
particleSystem.particles.forEach((particle) => {
let field = particle["physics"][physics].field(
lastPosition
);
totalField.add(field);
});
let newPosition = vec()
.copy(lastPosition)
.add(vec().copy(totalField).setLength(detail));
if (particleSystem.boundary.contains(newPosition)) {
positions.push(newPosition);
}
let willRestart = Math.random() < 0.05 ? true : false;
if (willRestart) {
let randomX =
(Math.random() - 1 / 2) * particleSystem.boundary.w +
particleSystem.boundary.x;
let randomY =
(Math.random() - 1 / 2) * particleSystem.boundary.h +
particleSystem.boundary.y;
let randomZ =
(Math.random() - 1 / 2) * particleSystem.boundary.d +
particleSystem.boundary.z;
positions = [vec(randomX, randomY, randomZ)];
}
if (line.current && positions.length > 2) {
let curve = new THREE.CatmullRomCurve3(positions).getPoints(
positions.length
);
let convertedToArray = [];
curve.forEach((item) => convertedToArray.push(item.x, item.y, item.z));
line.current.setPositions(convertedToArray);
}
});
return (
<>
<line2>
<lineGeometry ref={line} />
<lineMaterial
color={color}
linewidth={width}
resolution={new THREE.Vector2(10, 10)}
/>
</line2>
</>
);
}
P.S. vec() is just new THREE.Vector3(0,0,0)
I'm trying to create an array of meshes I added to my THREE.JS scene because I used a for-loop with the load() function to create the instances of the meshes, and I wanted to reference them later for the animate() function. The array shows a list of values in the console, but the length shows up as 0 when logged. This is the code I have:
const manager = new THREE.LoadingManager(); //loading manager
const loader = new GLTFLoader(manager);
let pos_arr = []
manager.onLoad = function(e) {
scene.traverse( function (model) {
if (model.isMesh) {
pos_arr.push(model)
}
})
}
let pos = new THREE.Vector3()
//spheres and equators
let ball = new THREE.Mesh();
let equator = new THREE.Mesh()
for (let i = 1; i <= 12; i++) {
let scale = Math.floor(Math.random() * 3) + 1
let pos_x = Math.floor(Math.random() * 10) * 5 - 25
let pos_y = Math.floor(Math.random() * 10) * 5 - 25
let pos_z = Math.floor(Math.random() * 10) * 5 - 25
pos = new THREE.Vector3(pos_x, pos_y, pos_z)
loader.load( './ball.gltf', function ( gltf ) { //load sphere
gltf.scene.traverse(function(model) {
if (model.isMesh) {
model.castShadow = true;
model.material = sphereMaterial;
}
});
ball = gltf.scene
ball.position.set(pos_x, pos_y, pos_z)
ball.scale.set(scale, scale, scale)
scene.add( ball );
}, undefined, function ( error ) {
console.error( error );
} );
loader.load( './equator.gltf', function ( gltf2 ) {
gltf2.scene.traverse(function(model) { //for gltf shadows!
if (model.isMesh) {
model.castShadow = true
model.material = equatorMaterial
}
});
equator = gltf2.scene
equator.position.set(pos_x, pos_y, pos_z)
equator.scale.set(scale, scale, scale)
scene.add( equator )
}, undefined, function ( error ) {
console.error( error )
} );
}
//light
const light = new THREE.AmbientLight( 0xffffff );
scene.add( light );
console.log(pos_arr)
console.log(pos_arr.length)
This is what the console shows:
I got a recommendation that I use the onLoad attribute for the LoadingManager, which I used, but it didn't work. If anyone happens to know a possible solution, it would be greatly appreciated :)
When I click on the spin button, I want the value to be highlighted one by one in the canvas.
Here you can see the whole code: https://codesandbox.io/s/spinning-wheel-react-forked-s528m
renderWheel() {
// determine number/size of sectors that need to created
let numOptions = this.state.list.length;
let arcSize = (2 * Math.PI) / numOptions;
this.setState({
angle: arcSize
});
// dynamically generate sectors from state list
let angle = 0;
for (let i = 0; i < numOptions; i++) {
let text = this.state.list[i];
this.renderSector(i + 1, text, angle, arcSize);
angle += arcSize;
}
}
renderSector(index, text, start, arc) {
// create canvas arc for each list element
let canvas = document.getElementById("wheel");
let ctx = canvas.getContext("2d");
let x = canvas.width / 2;
let y = canvas.height / 2;
let radius = 200;
let startAngle = start;
let endAngle = start + arc - 0.02;
let angle = index * arc;
let baseSize = radius * 1.25;
let textRadius = baseSize - 48;
ctx.beginPath();
ctx.arc(x, y, radius, startAngle, endAngle, false);
ctx.lineWidth = radius * 0.25;
ctx.strokeStyle = "white";
ctx.font = "25px Arial";
ctx.fillStyle = "blue";
ctx.stroke();
ctx.save();
ctx.translate(
baseSize + Math.cos(angle - arc / 2) * textRadius,
baseSize + Math.sin(angle - arc / 2) * textRadius
);
ctx.rotate(0);
ctx.fillText(text, -ctx.measureText(text).width / 2, 7);
ctx.restore();
}
When I click on the spin button, this spin () method is executed.
The iteration ends between two and five seconds as written in the setTimeout method.
spin = () => {
var index = 0;
let timer = setInterval(() => {
index = (index) % this.state.list.length + 1;
console.log(index)
}, 150);
// calcalute result after wheel stops spinning
setTimeout(() => {
clearInterval(timer);
this.setState({
hasFinishedLoading: true,
display: this.state.list[index]
});
}, Math.floor(Math.random() * 5000 + 2000));
this.setState({
spinning: true
});
};
Remember: In canvas you cannot access a particular object, so the solution is always to erase and redraw. Don't worry about performance, this is pretty great. I leave you an idea with your code and at the end some suggestions. Try this code, the only thing I did is clean the canvas and redraw without the setTimeOut index
spin = () => {
let canvas = document.getElementById("wheel");
let ctx = canvas.getContext("2d");
var index = 0;
index = (index % this.state.list.length) + 1;
let angle = 0;
let numOptions = this.state.list.length;
let arcSize = (2 * Math.PI) / numOptions;
let timer = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
this.setState({
angle: arcSize
});
for (let i = 0; i < numOptions; i++) {
if (i !== index) {
let text = this.state.list[i];
this.renderSector(i + 1, text, angle, arcSize);
angle += arcSize;
} else {
}
}
}, 150);
// calcalute result after wheel stops spinning
setTimeout(() => {
clearInterval(timer);
this.setState({
hasFinishedLoading: true,
display: this.state.list[index]
});
}, Math.floor(Math.random() * 5000 + 2000));
this.setState({
spinning: true
});
};
Don't use "getElementById" in react, it's dangerous. Use a reference.
I'm trying to create an array to localstorage containing some gear box values, witch I then need again to do some more calculations with specific array[?].value.
The problem I'm encountering is that the created entry returns only 2 array entries, although all entries are there but the " is at the beginning and then at the second last entry and then before and after the last entry.
These are the values generated.
spd_Dev = ["202.391,172.876,120.451,102.601,85.173,72.664,61.701,52.706,45.116,38.510,32.326,27.407,22.910,19.536,16.585,14.195,12.228","10.401"]
When I used to use a fixed array I've entered it as:
var spd_Dev = [202.391, 172.876, 120.451, 102.601, 85.173, 72.664, 61.701, 52.706, 45.116, 38.510, 32.326, 27.407, 22.910, 19.536, 16.585, 14.195, 12.228, 10.401];
So my question is how to write this values to localstorage and read again from localstorage so that I can use this values as an array.
This is what I'm trying to work.
var D_RatioId = data.truck.make + data.truck.model + "D_Ratio"
var G_RatioId = data.truck.make + data.truck.model + "G_Ratio"
var fGr = data.truck.forwardGears;
var gr = data.truck.displayedGear;
var Rpm = data.truck.engineRpm * 100;
var spd = data.truck.speed;
var T_Dia = 1008.3;
if (localStorage.getItem(G_RatioID) == undefined) {
localStorage.setItem(G_RatioID, '');
var fG_Rat = [localStorage.getItem(G_RatioID)];
} else {
var fG_Rat = [localStorage.getItem(G_RatioID)];
}
var spd_Dev = localStorage.getItem(Spd_DevId);
spd_Dev = spd_Dev ? spd_Dev.split(', ') : [];
if (localStorage.getItem("i") == undefined) {
localStorage.setItem("i", 1);
var i = localStorage.getItem("i");
} else {
var i = localStorage.getItem("i");
}
if (i <= fGr && spd > 0.5) {
if (i <= fGr + 1) {
if (i == gr) {
if (RPM > 1450) {
var G_Ratio = Math.abs(Rpm / D_Ratio * (Math.PI * T_Dia / 1000) * 60 / spd / 1000, 2).toFixed(2);
var spd_D = Math.abs(RPM / (RPM / G_Ratio / D_Ratio * (Math.PI * T_Dia / 1000) * 60 / 1000) * 0.821932).toFixed(3);
fG_Rat.push(G_Ratio);
spd_Dev.push(spd_D);
i++;
localStorage.setItem("i", i);
var G_RatioValue = fG_Rat;
var Spd_DevValue = spd_Dev;
SetG_Ratio(G_RatioID, G_RatioValue);
SetSpd_Dev(Spd_DevId, Spd_DevValue);
localStorage.setItem("spd_Dev", JSON.stringify(Spd_DevValue));
}
}
}
}
function SetG_Ratio(G_RatioID, G_RatioValue) {
localStorage.setItem(G_RatioID, G_RatioValue);
console.log(G_RatioID, G_RatioValue);
}
function SetSpd_Dev(Spd_DevId, Spd_DevValue) {
localStorage.setItem(Spd_DevId, Spd_DevValue.toString());
console.log(Spd_DevId, Spd_DevValue, Spd_DevValue.length);
}
if (spd >= 0) {
var spd_Dev = [localStorage.getItem(Spd_DevId)];
var spD_D = JSON.parse(spd_Dev);
for (i = 1; i < (fGr + 1); i++) { // Some more code dependant on the above results //
why don't you use LSM.js? it will make everything simpler. you can put the array as an array so that it will return the full array only, you will not need to use split(),
https://github.com/kevinj045/LSM_js
var lsm = new LSM("G_RatioID");
var G_RatioValue ["202.391,172.876,120.451,102.601,85.173,72.664,61.701,52.706,45.116,38.510,32.326,27.407,22.910,19.536,16.585,14.195,12.228","10.401"];
lsm.set("G_RatioID",G_RatioValue);
console.log(lsm.get("G_RatioID")); // it will return the full array
This is my second question today. I am using Cordova + angular (1).
I am trying to compare two geolocation with the help of distanceInKmBetweenCoordinates function.
I am getting this error while running the app.
angular.min.js:101 ReferenceError: distanceInKmBetweenCoordinates is
not defined
at Channel.onDeviceReady
Here is the code.
document.addEventListener('deviceready', function onDeviceReady() {
angular.bootstrap(document, ['formApp']);
}, false);
var formApp = angular.module('formApp', []);
// define angular module/app
var formApp = angular.module('formApp', []);
formApp.controller('formProfile1', function($scope,$http,$location, $timeout){
function degreesToRadians(degrees) {
return degrees * Math.PI / 180;
}
function distanceInKmBetweenEarthCoordinates(lat1, lon1, lat2, lon2) {
var earthRadiusKm = 6371;
var dLat = degreesToRadians(lat2-lat1);
var dLon = degreesToRadians(lon2-lon1);
lat1 = degreesToRadians(lat1);
lat2 = degreesToRadians(lat2);
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return earthRadiusKm * c;
}
I am using Cordova geolocation plugin to get the current location.
navigator.geolocation.getCurrentPosition(function(position) {
$scope.userlongitude = position.coords.longitude;
$scope.userlatitude = position.coords.latitude;
Below are some ajax request to get the data from the database including geolocation to compare.
$http({
url: 'https://www.abcd.xyz/get_info.php',
method: "GET",
params: {
tn_id: $scope.tn_id
}
})
.success(function(data) {
if (data.success) {
$scope.longitude = data.longitude;
$scope.latitude = data.latitude;
}
$scope.distance = distanceInKmBetweenCoordinates($scope.userlatitude, $scope.userlongitude, $scope.latitude, $scope.longitude);
In the end, I am comparing the geolocation.
Can you advise me what am I doing wrong? If I just do the compare only in angular then it is showing me correct value but along with Cordova, it is broken.
I also made changes in the body section as follows.
<!--<body id="home" ng-app="formApp"> --> Previous one
<body id="home">
Edit 1
I changed the code like this.
degreesToRadians = function(degrees) {
return degrees * Math.PI / 180;
}
distanceInKmBetweenEarthCoordinates= function(lat1, lon1, lat2, lon2) {
var earthRadiusKm = 6371;
var dLat = degreesToRadians(lat2-lat1);
var dLon = degreesToRadians(lon2-lon1);
lat1 = degreesToRadians(lat1);
lat2 = degreesToRadians(lat2);
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return earthRadiusKm * c;
}
$scope.distance = distanceInKmBetweenEarthCoordinates(43, 44,44,55);
alert($scope.distance);
and when I test it is giving me correct value but when I test it like this.
distanceInKmBetweenCoordinates($scope.userlatitude, $scope.userlongitude, 44,55);
then it is giving me the same error.
I used $timeout function to delay the loading but the error is same.
$timeout(function() {
$scope.distance = distanceInKmBetweenCoordinates($scope.userlatitude, $scope.userlongitude, 44,55);
alert($scope.distance);
console.log($scope.distance);
}, 5000);
As you suggested, place your code once you get the response from the ajax call,
if (data.success) {
$scope.longitude = data.longitude;
$scope.latitude = data.latitude;
$scope.distance = distanceInKmBetweenCoordinates($scope.userlatitude, $scope.userlongitude, $scope.latitude, $scope.longitude);
}
Try to assign the function to a local var in controller scope, maybe it's loosing reference:
formApp.controller('formProfile1', function($scope,$http,$location){
var degreesToRadians = function(degrees) {
return degrees * Math.PI / 180;
}
var distanceInKmBetweenEarthCoordinates = function(lat1, lon1, lat2, lon2) {
var earthRadiusKm = 6371;
var dLat = degreesToRadians(lat2-lat1);
var dLon = degreesToRadians(lon2-lon1);
lat1 = degreesToRadians(lat1);
lat2 = degreesToRadians(lat2);
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) *
Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
return earthRadiusKm * c;
}