<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>InfluxData Blog - Sean Brickley</title>
    <description>Posts by Sean Brickley on the InfluxData Blog</description>
    <link>https://www.influxdata.com/blog/author/sean-brickley/</link>
    <language>en-us</language>
    <lastBuildDate>Wed, 03 Aug 2022 07:00:00 +0000</lastBuildDate>
    <pubDate>Wed, 03 Aug 2022 07:00:00 +0000</pubDate>
    <ttl>1800</ttl>
    <item>
      <title>Outer Joins in Flux</title>
      <description>&lt;p&gt;Joins are a common transformation in any query language, and as part of the effort to make Flux an increasingly valuable tool for our users, the engineers on InfluxData’s query team created, and continue to maintain, two separate join functions. And while these solutions have met some of our users’ needs, they both lack one key feature: support for &lt;a href="https://www.diffen.com/difference/Inner_Join_vs_Outer_Join"&gt;outer joins&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This has been one of the most frequent requests from our community members, and perhaps one of the most notable drawbacks of using Flux in place of some other query language.&lt;/p&gt;

&lt;p&gt;That’s why the query team is happy to announce that, if you were one of the many community members waiting for outer join support in Flux, the wait is over! Flux has a new &lt;a href="https://docs.influxdata.com/flux/v0.x/stdlib/join/" target="_blank"&gt;&lt;code class="language-javascript code-link"&gt;join&lt;/code&gt; package&lt;/a&gt; that improves upon the previous two joins both in terms of performance and feature parity with other query languages, including (drum roll…) support for outer joins!&lt;/p&gt;

&lt;h2 id="use-the-join-package-in-your-flux-scripts"&gt;Use the join package in your Flux scripts&lt;/h2&gt;

&lt;p&gt;Import the join package by adding the following line to the beginning of your script:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-javascript"&gt;import "join"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code class="language-javascript"&gt;join&lt;/code&gt; package provides six new functions. I will use the &lt;a href="https://docs.influxdata.com/flux/v0.x/stdlib/join/tables/"&gt; &lt;code class="language-javascript code-link"&gt;tables()&lt;/code&gt;&lt;/a&gt; function for my code examples, because it’s the most flexible. The other functions use the same underlying implementation — they just set some default arguments for the sake of user convenience and code readability. So, if you already know what type of join you need for your query, you can save yourself some typing by using one of the following instead:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href="https://docs.influxdata.com/flux/v0.x/stdlib/join/inner/"&gt;join.inner()&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href="https://docs.influxdata.com/flux/v0.x/stdlib/join/full/"&gt;join.full()&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href="https://docs.influxdata.com/flux/v0.x/stdlib/join/left/"&gt;join.left()&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href="https://docs.influxdata.com/flux/v0.x/stdlib/join/right/"&gt;join.right()&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;a href="https://docs.influxdata.com/flux/v0.x/stdlib/join/time/"&gt;join.time()&lt;/a&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="define-your-inputs"&gt;Define your inputs&lt;/h2&gt;

&lt;p&gt;First, define your left and right input table streams. These can come from any source, but the key thing to remember is that join will only compare tables with the same group key when considering which rows to join. So, if your two inputs are grouped on different columns, or if their tables don’t share any group keys, you may not get the results you expect.&lt;/p&gt;

&lt;p&gt;Pass in your left and right inputs as arguments to the &lt;code class="language-javascript"&gt;left&lt;/code&gt; and &lt;code class="language-javascript"&gt;right&lt;/code&gt; parameters respectively. All of the functions in the &lt;code class="language-javascript"&gt;join&lt;/code&gt; package require that you explicitly set an argument for &lt;code class="language-javascript"&gt;right&lt;/code&gt;. The &lt;code class="language-javascript"&gt;left&lt;/code&gt; parameter, however, can be omitted in favor of pipe-forwarded data. The following two code blocks are functionally identical.&lt;/p&gt;

&lt;pre class="line-numbers"&gt;&lt;code class="language-javascript"&gt;import "join"

tbl_1 = from(...) |&amp;gt; ...
tbl_2 = from(...) |&amp;gt; ...

join.tables(
    left: tbl_1,
    right: tbl_2,
    ...
)&lt;/code&gt;&lt;/pre&gt;

&lt;pre class="line-numbers"&gt;&lt;code class="language-javascript"&gt;import "join"

tbl_1 = from(...) |&amp;gt; ...
tbl_2 = from(...) |&amp;gt; ...

tbl_1 |&amp;gt; join.tables(
    right: tbl_2,
    ...
)&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;The &lt;code class="language-javascript" style="font-size: 1.7rem;"&gt;‘on’&lt;/code&gt; function&lt;/h2&gt;

&lt;p&gt;Next, we need to define a function for the on parameter. The join transformation uses this function to compare records from the left and right inputs that have the same group key. If the function evaluates to &lt;code class="language-javascript"&gt;true&lt;/code&gt;, the two records being compared will be joined in the final output table.&lt;/p&gt;

&lt;p&gt;This function takes two arguments, a left record (&lt;code class="language-php"&gt;1&lt;/code&gt;) and a right record (&lt;code class="language-javascript"&gt;r&lt;/code&gt;), and the function body needs to follow a fairly strict format: a single boolean expression, consisting of one or more equality comparisons (&lt;code class="language-javascript"&gt;==&lt;/code&gt;) between a property of &lt;code class="language-php"&gt;1&lt;/code&gt; and a property of &lt;code class="language-javascript"&gt;r&lt;/code&gt;, chained together by the &lt;code class="language-javascript"&gt;and&lt;/code&gt; operator.&lt;/p&gt;

&lt;pre class="line-numbers"&gt;&lt;code class="language-javascript"&gt;import "join"

tbl_1 = from(...) |&amp;gt; ...
tbl_2 = from(...) |&amp;gt; ...

join.tables(
    left: tbl_1,
    right: tbl_2,
    on: (l, r) =&amp;gt; l._time == r._time and r.id == l.label
    ...
)&lt;/code&gt;&lt;/pre&gt;

&lt;h2&gt;Join methods and the &lt;code class="language-javascript" style="font-size: 1.7rem;"&gt;‘as’&lt;/code&gt; function&lt;/h2&gt;

&lt;p&gt;There are two arguments left to define, and it’s important to understand how they work together.&lt;/p&gt;

&lt;p&gt;The &lt;code class="language-javascript"&gt;method&lt;/code&gt; (as its name implies) determines the method used to join the two tables: &lt;code class="language-javascript"&gt;inner&lt;/code&gt;, &lt;code class="language-javascript"&gt;left&lt;/code&gt;, &lt;code class="language-javascript"&gt;right&lt;/code&gt;, or &lt;code class="language-javascript"&gt;full&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code class="language-javascript"&gt;as&lt;/code&gt; function defines the joined output of two matching rows. Similar to the &lt;code class="language-javascript"&gt;on&lt;/code&gt; function, it takes a left and right record (&lt;code class="language-php"&gt;1&lt;/code&gt; and &lt;code class="language-javascript"&gt;r&lt;/code&gt;, respectively) as its arguments. Its job is to return a new record built from the component properties of &lt;code class="language-php"&gt;1&lt;/code&gt; and &lt;code class="language-javascript"&gt;r&lt;/code&gt;. The returned record will be included in the final output of the join.&lt;/p&gt;

&lt;p&gt;Note that the return value of the &lt;code class="language-javascript"&gt;as&lt;/code&gt; function needs to maintain the group key of the inputs. If you define an output record that modifies or excludes the value of a group column, join returns an error.&lt;/p&gt;

&lt;p&gt;The details of how to build the output record vary slightly depending on the join method used. An inner join will skip any unmatched records, allowing us to define a fairly straightforward &lt;code class="language-javascript"&gt;as&lt;/code&gt; function:&lt;/p&gt;

&lt;pre class="line-numbers"&gt;&lt;code class="language-javascript"&gt;import "join"

tbl_1 = from(...) |&amp;gt; ...
tbl_2 = from(...) |&amp;gt; ...

join.tables(
    method: "inner"
    left: tbl_1,
    right: tbl_2,
    on: (l, r) =&amp;gt; l._time == r._time and r.id == l.label
    as: (l, r) =&amp;gt; ({l with r_val: r._value})
)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If, however, it’s performing any kind of outer join (i.e., &lt;code class="language-javascript"&gt;left&lt;/code&gt;, &lt;code class="language-javascript"&gt;right&lt;/code&gt;, or &lt;code class="language-javascript"&gt;full&lt;/code&gt;), we need to more carefully consider how we construct our output record.&lt;/p&gt;

&lt;p&gt;When &lt;code class="language-javascript"&gt;join&lt;/code&gt; finds a record in one input that doesn’t have a matching record in the other input, it will check the join method and, if appropriate, substitute a default record. A default record has the same schema that the missing record would, but only the group key columns are populated. All other values are null.&lt;/p&gt;

&lt;p&gt;So, when defining the return value of our &lt;code class="language-javascript"&gt;as&lt;/code&gt; function in an outer join, we need to pay special attention to where we pull its properties from.&lt;/p&gt;

&lt;p&gt;A left join will produce one or more output rows for every record in the left input. As a result, the l argument is guaranteed to not be a default record. We do not have the same guarantee for &lt;code class="language-javascript"&gt;r&lt;/code&gt;, so if we build our output record with any non-group-key values from &lt;code class="language-javascript"&gt;r&lt;/code&gt;, we should expect that those will sometimes be null.&lt;/p&gt;

&lt;p&gt;The opposite is true for a right join. In a right join, &lt;code class="language-javascript"&gt;r&lt;/code&gt; is guaranteed to not be a default record, while &lt;code class="language-php"&gt;1&lt;/code&gt; has no such guarantees. So, if we want to ensure that certain values will not be null in the output record, we should pull those values from &lt;code class="language-javascript"&gt;r&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Full outer joins are a little trickier. In a full outer join, it’s possible for either &lt;code class="language-php"&gt;1&lt;/code&gt; or &lt;code class="language-javascript"&gt;r&lt;/code&gt; to be a default record. The only guarantee we have is that they will never both be default records at the same time. One of them will have the non-null values we need for our output record, but we can’t be sure which one, so we have to check.&lt;/p&gt;

&lt;pre class="line-numbers"&gt;&lt;code class="language-javascript"&gt;import "array"
import "array"
import "join"

right =
    array.from(
        rows: [
            {_time: 2022-06-01T00:00:00Z, _value: 1, id: "a", key: 1},
            {_time: 2022-06-01T00:00:00Z, _value: 2, id: "b", key: 1},
            {_time: 2022-06-01T00:00:00Z, _value: 3, id: "d", key: 1},
        ],
    )
        |&amp;gt; group(columns: ["key"])
left =
    array.from(
        rows: [
            {_time: 2022-06-01T00:00:00Z, _value: 12.34, label: "a", key: 1},
            {_time: 2022-06-01T00:00:00Z, _value: 56.78, label: "c", key: 1},
            {_time: 2022-06-01T00:00:00Z, _value: 90.12, label: "d", key: 1},
        ],
    )
        |&amp;gt; group(columns: ["key"])

join.tables(
    method: "full",
    left: left,
    right: right,
    on: (l, r) =&amp;gt; l._time == r._time and l.label == r.id,
    as: (l, r) =&amp;gt; {
        time = if exists l._time then l._time else r._time
        label = if exists l.label then l.label else r.id

        return {
            label: label,
            v_left: l._value,
            key: r.key,
            v_right: r._value,
            _time: time,
        }
    },
)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In this example, we’re using the &lt;code class="language-javascript"&gt;exists&lt;/code&gt; keyword to see if &lt;code class="language-php"&gt;1&lt;/code&gt; or &lt;code class="language-javascript"&gt;r&lt;/code&gt; has the non-null values we need for the &lt;code class="language-javascript"&gt;_time&lt;/code&gt; and &lt;code class="language-javascript"&gt;label&lt;/code&gt; properties. We are still pulling values directly from &lt;code class="language-php"&gt;1&lt;/code&gt; and &lt;code class="language-javascript"&gt;r&lt;/code&gt; for &lt;code class="language-javascript"&gt;v_left&lt;/code&gt; and &lt;code class="language-javascript"&gt;v_right&lt;/code&gt; because we expect those to sometimes be null in the final output. We are also pulling the value for the &lt;code class="language-javascript"&gt;key&lt;/code&gt; property directly from &lt;code class="language-javascript"&gt;r&lt;/code&gt;, because it’s part of the group key and therefore guaranteed to be non-null. We could just as easily have used l.key instead.&lt;/p&gt;

&lt;p&gt;Running the example script above produces the following output:&lt;/p&gt;

&lt;p&gt;&lt;img src="//images.ctfassets.net/o7xu9whrs0u9/1cyGN95moVbc6R3tMgVuRP/5e70c9dfa876c032036cfc9441c46df2/script-output.jpg" alt="script-output" /&gt;&lt;/p&gt;

&lt;p&gt;As we would expect, the full outer join produces a joined row for every record in both inputs, even those without a match in the opposite table.&lt;/p&gt;

&lt;p&gt;Those are the basics of how to use the new Flux join package. We on the query team encourage you to try it out and give us your feedback!&lt;/p&gt;

&lt;p&gt;Keep in mind that, as with any open-source project, the maintenance and development of Flux is an ongoing effort, and the &lt;code class="language-javascript"&gt;join&lt;/code&gt; package is no exception. If you find that a function in the join package does not behave as expected, or doesn’t meet your needs, please let us know by either posting on our &lt;a href="https://community.influxdata.com/"&gt;community forums&lt;/a&gt; or by &lt;a href="https://github.com/influxdata/flux/issues/new"&gt;opening an issue in the Flux repository on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;p style="font-size: 15px;"&gt;&lt;sup&gt;1&lt;/sup&gt; Joining per group key allows us to, in many cases, significantly reduce the number of row comparisons the join transformation needs to make, and thereby improve performance.&lt;/p&gt;

&lt;p style="font-size: 15px;"&gt;&lt;sup&gt;2&lt;/sup&gt; You might wonder why the new join package uses a predicate function with such strictly enforced guidelines rather than just a list of column names. The main reason is that, perhaps ironically, this method offers the greatest flexibility. Not only can we compare columns with different names in the left and right tables, but we may be able to leverage this function in the future to allow for more complex join predicates.&lt;/p&gt;

&lt;p style="font-size: 15px;"&gt;For now, enforcing this format allows Flux to ensure that all joins are equi-joins, meaning the only criteria for whether or not two rows should be joined is the equality of two sets of values. As a result, the Flux query planner is able to translate this function into a series of column name pairs, which the join transformation then uses to compare records from the left and right tables, thereby saving on compilation time.&lt;/p&gt;

&lt;p style="font-size: 15px;"&gt;In the future, we may be able to relax the restrictions on the predicate function to allow for more complex comparisons. Doing the same for a version of join that instead used a list of column names as its predicate would be much more difficult.&lt;/p&gt;

&lt;p style="font-size: 15px;"&gt;&lt;sup&gt;3&lt;/sup&gt; …assuming those values were not null in the input table&lt;/p&gt;
</description>
      <pubDate>Wed, 03 Aug 2022 07:00:00 +0000</pubDate>
      <link>https://www.influxdata.com/blog/outer-joins-flux/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/outer-joins-flux/</guid>
      <category>Product</category>
      <category>Use Cases</category>
      <category>Developer</category>
      <author>Sean Brickley (InfluxData)</author>
    </item>
    <item>
      <title>Using the New Flux "types" Package</title>
      <description>&lt;p&gt;&lt;img class="aligncenter wp-image-265777" src="/images/legacy-uploads/Flux-Query-1-1.png" alt="Flux Query 1" width="980" height="582" /&gt;&lt;/p&gt;

&lt;p&gt;As a strictly typed language, Flux protects you from a lot of potential runtime failures. However, if you don’t know the column types on the data you’re querying, you might encounter some annoying errors.&lt;/p&gt;

&lt;p&gt;Suppose you have a bucket that receives regular writes from multiple different streams, and you want to write a task to downsample a measurement from that bucket into another bucket. If you know ahead of time that, for example, the &lt;code class="language-markup"&gt;_value&lt;/code&gt; column will always be a numeric type, you could run the following task without any problems:&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-javascript"&gt;option task = {name: "write-agg", every: 30m, offset: 1s}

from(bucket: "test-bucket")
       |&amp;gt; range(start: -30m)
       |&amp;gt; filter(fn: (r) =&amp;gt; r._measurement == "logs")
       |&amp;gt; aggregateWindow(fn: mean, every: 5m)
       |&amp;gt; to(bucket: "test-downsample")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But if you don’t know the schema of the data before you query it, you could run into trouble. It’s possible for this query to fail if, for instance, &lt;code class="language-markup"&gt;_value&lt;/code&gt; turns out to be a string instead of a number.&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone wp-image-265778" src="/images/legacy-uploads/could-not-execute-task.png" alt="could-not-execute-task" width="700" height="102" /&gt;&lt;/p&gt;

&lt;p&gt;Until now, there has not been a one-size-fits-all solution to this problem. A filter on &lt;code class="language-markup"&gt;_field&lt;/code&gt; could do the trick if you know the labels you’re looking for, but maybe you don’t know those details, or maybe the list of labels you need to include or exclude is too long to comfortably fit in a filter predicate.&lt;/p&gt;

&lt;p&gt;Enter the &lt;code class="language-markup"&gt;types&lt;/code&gt; package. This package introduces the &lt;code class="language-markup"&gt;isType&lt;/code&gt; function, which makes filtering on column types much easier. We can use it to fix our original query by importing the &lt;code class="language-markup"&gt;types&lt;/code&gt; package and adding a new filter that checks the type of &lt;code class="language-markup"&gt;r._value&lt;/code&gt;.&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-javascript"&gt;import "types"

option task = {name: "write-agg", every: 30m, offset: 1s}

from(bucket: "test-bucket")
       |&amp;gt; range(start: -30m)
       |&amp;gt; filter(fn: (r) =&amp;gt; r._measurement == "logs")
       |&amp;gt; filter(fn: (r) =&amp;gt; types.isType(v: r._value, type: "float"))
       |&amp;gt; aggregateWindow(fn: mean, every: 5m)
       |&amp;gt; to(bucket: "test-downsample")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can be sure that any data piped into &lt;code class="language-markup"&gt;aggregateWindow&lt;/code&gt; has a &lt;code class="language-markup"&gt;_value&lt;/code&gt; column of type &lt;code class="language-markup"&gt;float&lt;/code&gt;, and thus avoid any potential type errors. Sure enough, our task succeeds and successfully writes the downsampled data to our new bucket:&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone wp-image-265779" src="/images/legacy-uploads/completed-task.png" alt="completed-task message" width="400" height="84" /&gt;&lt;/p&gt;

&lt;p&gt;We can also do more complex filtering using &lt;code class="language-markup"&gt;isType&lt;/code&gt;. Let’s imagine that the &lt;code class="language-markup"&gt;logs&lt;/code&gt; measurement we’re reading from the task above has fields with many different types. We want to aggregate all of them, but we know that some aggregates won’t work for every type. We can use &lt;code class="language-markup"&gt;isType&lt;/code&gt; to decide which aggregate function to use based on the type of data we find.&lt;/p&gt;
&lt;pre class="line-numbers"&gt;&lt;code class="language-javascript"&gt;import "types"

option task = {name: "write-agg", every: 30m, offset: 1s}

from(bucket: "test-bucket")
       |&amp;gt; range(start: -30m)
       |&amp;gt; filter(fn: (r) =&amp;gt; r._measurement == "logs")
       |&amp;gt; filter(fn: (r) =&amp;gt; {
                 return types.isType(v: r._value, type: "float")
                         or types.isType(v: r._value, type: "int")
                         or types.isType(v: r._value, type: "uint")
       })	
       |&amp;gt; aggregateWindow(fn: mean, every: 5m)
       |&amp;gt; to(bucket: "test-downsample")

from(bucket: "test-bucket")
       |&amp;gt; range(start: -30m)
       |&amp;gt; filter(fn: (r) =&amp;gt; r._measurement == "logs")
       |&amp;gt; filter(fn: (r) =&amp;gt; {
                return types.isType(v: r._value, type: "string")
                        or types.isType(v: r._value, type: "bool")
       })
      |&amp;gt; aggregateWindow(fn: last, every: 5m)
      |&amp;gt; to(bucket: "test-downsample")&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This new package is included in the latest version of Flux, and is available to all cloud users. We encourage you to test it out and let us know what you think!&lt;/p&gt;
</description>
      <pubDate>Mon, 07 Mar 2022 04:00:37 -0700</pubDate>
      <link>https://www.influxdata.com/blog/using-new-flux-types-package/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/using-new-flux-types-package/</guid>
      <category>Use Cases</category>
      <category>Developer</category>
      <category>Product</category>
      <author>Sean Brickley (InfluxData)</author>
    </item>
    <item>
      <title>Tracking the International Space Station Using InfluxDB</title>
      <description>&lt;p&gt;When I started my internship here at InfluxData, I was told that, in addition to my day-to-day work, I could start a personal side-project to work on throughout the summer – something that would help me get acquainted with the TICK Stack, and teach me something new in the process.&lt;/p&gt;

&lt;p&gt;Inspired by the recent collaboration between NASA and SpaceX, I decided that it might be fun (and educational) to track the position of the International Space Station, write that data to an InfluxDB instance, and draw it on a map. I found a &lt;a href="http://open-notify.org/Open-Notify-API/ISS-Location-Now/"&gt;public API&lt;/a&gt; with the data that I wanted, so I wrote &lt;a href="https://github.com/scbrickley/iss"&gt;a small program&lt;/a&gt; that queried that data, formatted it as line protocol, and wrote it to my local OSS instance.&lt;/p&gt;

&lt;p&gt;Then I hit a snag. I wanted to draw the stored coordinates to a world map, but by definition, a time series database is something that keeps track of data where the x-axis is almost always time. I was left with something that looked like this:&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone size-full wp-image-248760 aligncenter" src="/images/legacy-uploads/track-international-space-station-influxdb.png" alt="track international space station influxdb" width="938" height="232" /&gt;&lt;/p&gt;

&lt;p&gt;While this graph is interesting and informative in its own way, it doesn’t tell quite the same story as a map. I was disappointed to realize that there was nothing in InfluxData’s offerings that let me plot latitude and longitude measurements as coordinates over time.&lt;/p&gt;

&lt;p&gt;That is, until I was introduced to Flux’s experimental &lt;a href="https://docs.influxdata.com/flux/v0.x/stdlib/experimental/geo/" target="_blank" rel="noopener"&gt;geo&lt;/a&gt; package by our Vice President of Products, Tim Hall. During his talk at &lt;a href="https://www.influxdays.com/past-events-london-2020/sessions/"&gt;InfluxDays&lt;/a&gt;, Tim walked through the various additions to Telegraf and InfluxDB over the past 6 months or so which lay the foundation for geo-temporal data acquisition and analysis. (&lt;a href="https://www.youtube.com/watch?v=snLIFHmV5GU"&gt;You can see that video here&lt;/a&gt;…time index [23:50-33:00]). But Tim also gave me access to an experimental InfluxDB instance that had an exciting new visualization which provided just what I was looking for! Using these new tools, setting up a dashboard that accomplished what I wanted was as easy as clicking a few buttons, and writing this Flux script:&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone size-full wp-image-248761 aligncenter" src="/images/legacy-uploads/flux-geo-package.png" alt="flux geo package" width="938" height="169" /&gt;&lt;/p&gt;

&lt;p&gt;And here’s the map:&lt;/p&gt;

&lt;p&gt;&lt;img class="alignnone size-full wp-image-248762 aligncenter" src="/images/legacy-uploads/flux-geo-package-map.png" alt="flux geo package map" width="938" height="419" /&gt;&lt;/p&gt;

&lt;p&gt;As mentioned above, the features are still experimental, and there may be some kinks to work out before they’re ready for general adoption, but the engineers here are clearly working hard on this, and the results so far are promising. Stay tuned for updates!&lt;/p&gt;
</description>
      <pubDate>Fri, 24 Jul 2020 06:00:01 -0700</pubDate>
      <link>https://www.influxdata.com/blog/tracking-the-international-space-station-using-influxdb/</link>
      <guid isPermaLink="true">https://www.influxdata.com/blog/tracking-the-international-space-station-using-influxdb/</guid>
      <category>Product</category>
      <category>Use Cases</category>
      <category>Developer</category>
      <author>Sean Brickley (InfluxData)</author>
    </item>
  </channel>
</rss>
