I am creating a web page that has a Heat Map on it using Azure Maps.
I have the heat map successfully pulling in points and displaying, but I need to change how it is rendering.
Specifically my heat map needs to be based on length of time in an area - which can be accomplished by looking at the timestamp that is on my data points. Any ideas on how to accomplish this in the heat map layer using DateTime like values?
The HeatMapLayer class has an option called weight that can take a data driven style expression that provides a number between 0 and 1 to indicate the weight. In your case you are working with timestamps, so you will first want to decide what your time range for min and max values should be. If your timestamps are strings, you should loop through your data before adding it to the data source and calculate the integer value for the data and store it as a property in the feature (i.e. time). For example: new Date('2022-2-14T03:24:00').getTime()
For example, you might want the max value to be anything that has occurred in the current minute (0ms), and the min value to occur on anything that is 15 minutes (900,000ms) or older. You can then put together a formula that makes use of the integer date to get a scalar value between 0 and 1. Here is the math using plain JavaScript:
var timeRange = 900000; //15 minutes
var now = Date.now();
var time = new Date('2022-2-14T03:24:00').getTime();
//Calculate the time offset and snap to the 0 to 15 minute range.
var dt = Math.max(0, Math.min(timeRange, now - time))
//We want the inverse weight since smaller dt should be higher. Thus 1 minus ratio.
var weight = 1 - dt/timeRange;
This can then be turned into a data driven expression and set as the weight value in the heat map layer options.
var timeRange = 900000; //15 minutes
var now = Date.now();
map.layers.add(new atlas.layer.HeatMapLayer(datasource, null, {
weight: ['-', 1, ['/', ['max', 0, ['min', ['-', now, ['get', 'time']], timeRange]], timeRange]]timeRange]]
}), 'labels');
If you want to update the now value periodically you can rebuild the expression and update the heat map using the setOptions function. For example;
var timeRange = 900000; //15 minutes
function getWeightExp(){
var now = Date.now();
return ['-', 1, ['/', ['max', 0, ['min', ['-', now, ['get', 'time']], timeRange]], timeRange]];
}
//Create heatmap layer.
var heatMap = new atlas.layer.HeatMapLayer(datasource, null, {
//Set weight option.
weight: getWeightExp()
});
//Add the heatmap to the map.
map.layers.add(heatMap, 'labels');
//Set an update frequency. In this case once a minute.
setTimeout(function() {
heatMap.setOptions({
weight: getWeightExp()
});
}, 60000);
Related
I am querying the PostGres database using POSTGIS extension as I want to get users in a radius.
I am following this post, where the following code is working fine:
Alert.findAll({
where: Sequelize.where(
Sequelize.fn('ST_DWithin',
Sequelize.col('position'),
Sequelize.fn('ST_SetSRID',
Sequelize.fn('ST_MakePoint',
req.query.long, req.query.lat),
4326),
0.032),
true)
})
.then(alerts => res.send(alerts))
.catch(next)
But I am encountering a strange problem, the radius is defined in degrees not miles, e.g. they said
Sample query to find all within 0.032 deg which is approximately 2 miles.
Then to customize the radius & validate my solution against above statement, I searched a lot and I wasn't able to find any formula to convert my miles into radius say I want to check in the radius of 10 miles. Then I checked this stack question, and run some calculations based on the formulas given in the accepted answer, using miles as 2 instead of 100, just to ensure if the approximation is correct but the answer was 0.42 not 0.32. e.g.
const latTraveledMiles = 2;
const latTraveledKM = 2 * 0.621371;
const latTraveledDeg = (1 / 110.54) * latTraveledKM;
const currentLat = 74.0064;
const longTraveledMiles = 2;
const longTraveledKM = 2 * 0.621371;
const longTraveledDeg = (1 / (111.320 * Math.cos(currentLat))) * longTraveledKM;
let degrees = Math.sqrt(Math.pow(latTraveledDeg, 2), Math.pow(longTraveledDeg, 2)); //should be 0.32
To make things easier, I would suggest you cast the geometry column position and ST_MakePoint to geography which will allow you to use metric input. You can read more about it here https://www.crunchydata.com/blog/postgis-and-the-geography-type
To add a geography column to your table you can use the following statement
-- first you add the column of type geography
ALTER TABLE your_table ADD COLUMN geog geography(point, 4326);
-- afterwards you populate it from your geometry column which is position
UPDATE your_table SET geog = position::geography;
You can now use ST_DWithin(geography gg1, geography gg2, double precision distance_meters); in your javascript logic, hence just substitute your radius value with 2*1.609344*1000 which translates to 2 miles times the factor to kilometers times 1000 to yield that value in meters.
"StartingTime":"11:00",
"EndingTime":"5:00"
Hello,i have a JSON response in which i have these two strings.What i want to do is I want to make time slots using these startingTime and EndingTime.BTW,these two can change for different responses.I want to make time slots with 2 hrs difference between them.Also I want to add an extra 2 hour after the EndingTime.
Example:
Startime = 11:00
EndingTime = 5:00
Time Slots I need = 11:00-1:00 , 1:00-3:00 , 3:00-5:00 , 5:00-7:00
Also once I get this time slots I want to store and add them in a spinner.
How can I achieve it.Thanks.
You can make a simple data class to represent a time slot.
data class TimeSlot(val startTime: LocalTime, val endTime: LocalTime)
And then write a function that splits it up into as many slots that will fit:
fun TimeSlot.divide(lengthHours: Long): List<TimeSlot> {
require(lengthHours > 0) { "lengthHours was $lengthHours. Must specify positive amount of hours."}
val timeSlots = mutableListOf<TimeSlot>()
var nextStartTime = startTime
while (true) {
val nextEndTime = nextStartTime.plusHours(lengthHours)
if (nextEndTime > endTime) {
break
}
timeSlots.add(TimeSlot(nextStartTime, nextEndTime))
nextStartTime = nextEndTime
}
return timeSlots
}
Note, this simple comparison nextEndTime > endTime won't handle a time range that crosses midnight. You'd have to make this a little more complicated if you want to handle that.
You can look up in other existing questions how to parse the JSON values into LocalTimes and how to populate a Spinner from a List.
I currently have a game replay file I'm analyzing and was wondering the best way to store the data so I can create complex queries which are fast. For example, every 50 milliseconds the analyzer returns a data structure in which you can access a snapshot of the current round details and current players status in the game such has what weapon hes holding, how much health he has, which players he has currently shot, etc. I want the ability to say: From the start of the replay file to 10000 milliseconds in, what were the player "Micheal" positions. How much damage has the player "Kyle" to other players from 10000ms to 20000ms in. I want the ability to store all the data im analyzing and replay it on a frontend using a API so you can visually replay it.
I can store metadata about the replay into the database such as: (Round 1, StartTime: 10000, EndTime: 30000), (Round 2, StartTime: 31000, EndTime: 37000). I can also store meta data on when a player was killed (Kyle, DeathTime: 31000, KilledBy: Micheal) or when a player was hurt (Kyle, HurtBy: Micheal, Damage: 10, Weapon: x).
To accomplish what I want to do of being able to create complex queries for different situations, do I need a combination of the two? Such as storing the millisecond by millisecond data in like a NoSql database / Document and then also parsing the total file and storing metadata like mentioned in the second paragraph into another database. Is it not feasible to store only the millisecond by millisecond data and then have the ability to create fast queries to parse what I want from it?
Sounds like a cool game that you are working on. The Microsoft Azure Table Storage (a NoSQL database) is very fast and would be perfect for your game. You can use Slazure (I coded Slazure BTW) which has a query language (a custom LINQ provider) included and which also stores the data in the Azure Table Storage. Here is how I would do it if I was using Slazure and the Microsoft Azure Table Storage NoSQL database for your game. I suggest that you would store one row in the PlayersTable table for each event where the PartitionKey is the player name and the RowKey is how many milliseconds into the game round you are - since each table is indexed on the PartitionKey column and sorted by the RowKey column all queries using these columns are very fast indeed. There is also columns created for the weapon used, position on the screen, health, round number, and which player was killed by whom. You could create a table to hold damage data using the same proposed method as below:
using SysSurge.Slazure;
using SysSurge.Slazure.Linq;
using SysSurge.Slazure.Linq.QueryParser;
namespace TableOperations
{
public class PlayerInfo
{
// List of weapons
public enum WeaponList {
Axe, Arrow, Knife, Sling
};
// Update a player with some new data
public void UpdatePlayerData(dynamic playersTable, DateTime gameStartedTime, int round, string playerName, WeaponList weapon, int healthPoints, int xPos, int yPos)
{
// Create an entity in the Players table using the player name as the PartitionKey, the entity is created if it doesn't already exist
var player = playersTable.Entity(playerName);
// Store the time the event was recorded for later as milliseconds since the game started.
// This means there is one row for each stored player event
player.Rowkey = ((DateTime.UtcNow.Ticks - gameStartedTime.Ticks)/10000).ToString("d19");
player.Round = round; // Round number
player.Weapon = (int)weapon; // Weapon carried by player. Example Axe
// Store player X and Y position coordinates on the screen
player.X = xPos;
player.Y = yPos;
// Number of health points, zero means player is dead
player.HealthPoints = healthPoints;
// Save the entity to the Azure Table Service storage
player.Save()
}
// Update a player with some new data
public void PlayerKilled(dynamic playersTable, DateTime gameStartedTime, int round, string playerName, string killedByPlayerName)
{
// Create an entity in the Players table using the player name as the PartitionKey, the entity is created if it doesn't already exist
var player = playersTable.Entity(playerName);
// Store the time the event was recorded for later as milliseconds since the game started.
// This means there is one row for each stored player event
player.Rowkey = ((DateTime.UtcNow.Ticks - gameStartedTime.Ticks)/10000).ToString("d19");
player.Round = round; // Round number
// Number of health points, zero means player is dead
player.HealthPoints = 0;
player.KilledByPlayerName = killedByPlayerName; // Killed by this player, example "Kyle"
// Save the entity to the Azure Table Service storage
player.Save()
}
// Get all the player positions between two time intervals
public System.Linq.IQueriable GetPlayerPositions(dynamic playersTable, string playerName, int fromMilliseconds, int toMilliseconds, int round)
{
return playersTable.Where("PrimaryKey == #0 && RowKey >= #1 && RowKey <= #2 && Round == #3",
playerName, fromMilliseconds.ToString("d19"), toMilliseconds.ToString("d19"), round).Select("new(X, Y)");
}
}
}
First you need to record when the game started and the round number:
var gameStartedTime = DateTime.UtcNow;
var round = 1; // Round #1
, and create a table in the NoQL database:
// Get a reference to the Table Service storage
dynamic storage = new DynStorage("UseDevelopmentStorage=true");
// Get reference to the Players table, it's created if it doesn't already exist
dynamic playersTable = storage.Players;
Now, during the game you can continuously update the player information like so:
UpdatePlayerData(playersTable, gameStartedTime, round, "Micheal", WeaponList.Axe, 45, 12313, 2332);
UpdatePlayerData(playersTable, gameStartedTime, round, "Kyle", WeaponList.Knife, 100, 13343, 2323);
If you need to wait 50 ms between each storage event you can do something like:
System.Threading.Thread.Sleep(50);
, and then store some more player event data:
UpdatePlayerData(playersTable, gameStartedTime, round, "Micheal", WeaponList.Axe, 12, 14555, 1990);
UpdatePlayerData(playersTable, gameStartedTime, round, "Kyle", WeaponList.Sling, 89, 13998, 2001);
When one of the players has died you can call the same method with zero health points and the name of the player that killed him/her:
PlayerKilled(playersTable, gameStartedTime, round, "Micheal", "Kyle");
Now, later in your game analyser you can query for all the positions from the start of the game (0 ms) to 10,000 ms into the game as follows:
// Get a reference to the table storage and the table
dynamic queryableStorage = new QueryableStorage<DynEntity>("UseDevelopmentStorage=true");
QueryableTable<DynEntity> queryablePlayersTable = queryableStorage.PlayersTable;
var playerPositionsQuery = GetPlayerPositions(queryablePlayersTable, "Micheal", 0, 10000, round);
// Cast the query result to a dynamic so that we can get access its dynamic properties
foreach (dynamic player in playerPositionsQuery)
{
// Show player positions in the console
Console.WriteLine("Player position: Name=" + player.PrimaryKey + ", Game time MS " + player.RowKey + ", X-position=" + player.X + ", Y-position=" + player.Y;
}
I need to determine the duration between now and the next occurrance of a local time. Here's what I've got:
Duration GetDuration(IClock clock, LocalTime time, DateTimeZone zone)
{
// get the current time in this zone
var now = clock.Now.InZone(zone);
// find the time in our zone today
var timeToday = zone.AtLeniently(now.Date + time);
// find the time in our zone tomorrow
var timeTomorrow = zone.AtLeniently(now.Date.PlusDays(1) + time);
// get the next occurrance of that time
var next = new[]{timeToday, timeTomorrow}.Where(x => x > now).Min();
// calculate the duration between now and the next occurance of that time
var duration = next.ToInstant() - now.ToInstant();
Debug.Assert(duration > Duration.Zero);
return duration;
}
And a test to get the duration between now and the next instant of 5PM eastern time:
var duration = GetDuration(
SystemClock.Instance,
new LocalTime(17,00),
DateTimeZoneProviders.Tzdb["US/Eastern"]);
My question is, since I'm new to NodaTime, am I taking any unnecessary steps or missing any shortcuts?
This code is a bit more long-winded than it needs to be - I'd do most of the work in the "local" context, and only convert back to the time zone when you know the LocalDateTime you want to map.
var now = clock.Now.InZone(zone);
// Change this to <= time if you want it to be a "strictly next".
var nextDate = now.TimeOfDay < time ? now.Date : now.Date.PlusDays(1);
return zone.AtLeniently(nextDate + time).ToInstant() - now.ToInstant();
AtLeniently will always return a value which is no earlier than the given LocalDateTime (it returns the later of two ambiguous options, and the start of the interval after a skipped time), so you don't need to worry about DST transitions.
As an aside, feedback about whether the ToInstant() calls in the last line are annoying would be useful. I'd at least consider adding a Duration operator-(ZonedDateTime, ZonedDateTime) to 2.0.
The most easy way is like this:
LocalDate now = LocalDate.now();
LocalDate now2 = now.plusDays(4);
ChronoUnit.DAYS.between(now, now2);
I have records with a time value and need to be able to query them for a span of time and return only records at a given interval.
For example I may need all the records from 12:00 to 1:00 in 10 minute intervals giving me 12:00, 12:10, 12:20, 12:30, ... 12:50, 01:00. The interval needs to be a parameter and it may be any time value. 15 minutes, 47 seconds, 1.4 hours.
I attempted to do this doing some kind of reduce but that is apparently the wrong place to do it.
Here is what I have come up with. Comments are welcome.
Created a view for the time field so I can query a range of times. The view outputs the id and the time.
function(doc) {
emit([doc.rec_id, doc.time], [doc._id, doc.time])
}
Then I created a list function that accepts a param called interval. In the list function I work thru the rows and compare the current rows time to the last accepted time. If the span is greater or equal to the interval I add the row to the output and JSON-ify it.
function(head, req) {
// default to 30000ms or 30 seconds.
var interval = 30000;
// get the interval from the request.
if (req.query.interval) {
interval = req.query.interval;
}
// setup
var row;
var rows = [];
var lastTime = 0;
// go thru the results...
while (row = getRow()) {
// if the time from view is more than the interval
// from our last time then add it.
if (row.value[1] - lastTime > interval) {
lastTime = row.value[1];
rows.push(row);
}
}
// JSON-ify!
send(JSON.stringify({'rows' : rows}));
}
So far this is working well. I will test against some large data to see how the performance is. Any comments on how this could be done better or would this be the correct way with couch?
CouchDB is relaxed. If this is working for you, then I'd say stick with it and focus on your next top priority.
One quick optimization is to try not to build up a final answer in the _list function, but rather send() little pieces of the answer as you know them. That way, your function can run on an unlimited result size.
However, as you suspected, you are using a _list function basically to do an ad-hoc query which could be problematic as your database size grows.
I'm not 100% sure what you need, but if you are looking for documents within a time frame, there's a good chance that emit() keys should primarily sort by time. (In your example, the primary (leftmost) sort value is doc.rec_id.)
For a map function:
function(doc) {
var key = doc.time; // Just sort everything by timestamp.
emit(key, [doc._id, doc.time]);
}
That will build a map of all documents, ordered by the time timestamp. (I will assume the time value is like JSON.stringify(new Date), i.e. "2011-05-20T00:34:20.847Z".
To find all documents within, a 1-hour interval, just query the map view with ?startkey="2011-05-20T00:00:00.000Z"&endkey="2011-05-20T01:00:00.000Z".
If I understand your "interval" criteria correctly, then if you need 10-minute intervals, then if you had 00:00, 00:15, 00:30, 00:45, 00:50, then only 00:00, 00:30, 00:50 should be in the final result. Therefore, you are filtering the normal couch output to cut out unwanted results. That is a perfect job for a _list function. Simply use req.query.interval and only send() the rows that match the interval.