DataviewJS Snippet Showcase

Show family members, friends and places that are nearby

I love location data, maps, and visualizing stuff. When I plan to travel or visit a friend or family member, I love to see “what’s near” in each person or location note. Automatically linking places and people together, so to say.


“Nearby” with German setting, in kilometres (text untranslated; check ‘km’ and the decimal comma)

  • If I visit a friend, I can check if there are other friends or places of interest nearby, and we could maybe visit them together.
  • I can check if other friends are near enough to do something together, maybe meet for a barbeque.
  • When sightseeing, I won’t miss nearby friends, business contacts, favourite restaurants or other sights anymore.

Goals

  • Never miss anything nearby anymore.
  • Have this dataview available as a template so it can be in every person, business contact, and place-of-interest note and automatically show me what’s near this place.
  • No more people and location notes without location data (easily be copied from Google Maps or the like).

Requirements

  • All people/location notes must have a
    location: [lat, lon]
    
    entry in the YAML frontmatter. (This syntax has something to do with YAML & JS data structure and easy parsing. The sequence [lat, lon] has been chosen for practical reasons: People are used to it and it can easily be copied from Google Maps and others.)
  • Dataview 0.3.0+ installed.
  • moment.js available (it currently is).
  • Distance units of m, km, yd and mi must be usable and easy to switch.
  • Distances must be calculated as correct straight-line distances, taking into account that Earth is not a perfect sphere but a rotational ellipsoid.
  • Distances must also be available as (approximate; for speed and offline use) driving distances.
  • Distances shall be shown with the unit appended but internally numeric, for easy calculation/comparison.
  • Numbers shall be formatted according to the language currently set in Obsidian (4,096.1 vs 4.096,1). The OS’s system setting shall only be used if all else fails.
  • Date & time strings must be easily formatable however I wish, defaulting to ISO-8601 (2021-05-08T10:34:05+02:00).
  • Very long note title links shall optionally be shortened in Dataview table views, using my dataview-shorten-links.css snippet.
  • Tags (from frontmatter only, a current Dataview restriction) shall be shown in one (comma- or blank-)separated string, not as a list.
  • The list shall be sorted by distance from “here” (i.e., this note).
  • The distance values shall be clickable and lead to the Google Maps Route Planner, destination already filled in, and start point taken from the current location.
  • Above the table, a text line shall show the currently set “nearby” radius as well as the selected unit of measurement (within 100 km driving distance, within 50 mi straight-line distance), so we later know what’s actually shown in the table.

Current status

  • Proof-of-concept, working but params have to be set in the codeblock (instead of being taken from YAML).
  • It is still already usable, and should have no major bugs.


“Nearby” with English setting, in miles (check ‘mi’ and the decimal point)

To-Do

  • [x] Wait for new Dataview version that allows reading from “this” note, and other things.
  • [x] Make unit and radius readable from “this” note, most probably by using something like:
    nearby: 50 km
    
    or
    nearby: 50 mi
    
    which would split the string and use the numeric part as radius, the textual part as the desired unit for calculating & showing in the table.
  • [x] Read origin (starting point) from “this” note so all can be automatic, even when the location changes.
  • [x] Possibly make the searchterm for dv.pages() available in YAML. Don’t know yet.

Current Code

This is surely the part you’ve been waiting for! :grin:

Note: I might change the code here when updating it, so check more often. It’s easier than to keep a zillion copies down below.

This is the dataviewjs code block only. You’ll have to type your own header. :wink:

// Nearby Family Members, Friends and Places
// 2021-05-15 - Matthias C. Hormann (Moonbase59)

// set parameters (to be supplied via YAML frontmatter, eventually)
// DV 0.3.3 interprets "500000 m" as a Luxon duration,
// so we have to put nearby: '"500000 m"' and remove the ".
let nearby = dv.current().nearby.replace(/['"]+/g, '');
let unit = nearby.split(' ')[1];
let radius = nearby.split(' ')[0];
let origin = dv.current().location;

// search term as used in dv.pages()
// use "#tags" or '"folder/subfolder"' for a folder
let searchterm = dv.current().searchterm;

//======================================================================

function getDistance(origin, destination, unit='m') {
    // return distance in selected unit (m,km,yd,mi)
    var factor = 1.0;
    switch (unit) {
      case 'm':
        factor = 1.0;
        break;
      case 'km':
        factor = 1000.0;
        break;
      case 'yd':
        factor = 0.9144;
        break;
      case 'mi':
        factor = 1609.344;
        break;
      default:
        factor = 1.0;
        console.warn("getDistance: Invalid unit '%s', using 'm'. Valid units are: m,km,yd,mi.",unit);
    }

    var lon1 = toRadian(origin[1]),
        lat1 = toRadian(origin[0]),
        lon2 = toRadian(destination[1]),
        lat2 = toRadian(destination[0]);

    var deltaLat = lat2 - lat1;
    var deltaLon = lon2 - lon1;

    var a = Math.pow(Math.sin(deltaLat/2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(deltaLon/2), 2);
    var c = 2 * Math.asin(Math.sqrt(a));
    var EARTH_RADIUS = 6371000; // 6,371 km in metres
    return c * EARTH_RADIUS / factor;
}

function toRadian(degree) {
    return degree*Math.PI/180;
}

function getDrivingDistance(origin, destination, unit='m') {
    // PREDICTED, using a factor.
    // Much faster, and no need for Internet access.
    // The U.S. predict 1.417, based on statistical analysis.
    // For Germany, I’ve found 1.3 a good value (comparing w/ Google Maps "best route")
    var factor = 1.3;
    return getDistance(origin, destination, unit) * factor;
}

//======================================================================

// Show what we will display.
dv.paragraph("List shows nearby places (within " + radius + " " + unit + " driving distance).");
dv.paragraph("<br><br>");

// get the pages
let pages = dv.pages(searchterm)
  .where(p => p.location &&
    // must be >0 so we don’t catch ourselves!
    getDrivingDistance(origin, p.location, unit) > 0.0 &&
    getDrivingDistance(origin, p.location, unit) <= radius)
  .sort(p => getDistance(origin, p.location, unit));

// create table
dv.table(["Name", "Tags", "Distance*"],
  pages.map(p => [
    // The name
    p.file.link,
    // tags (show '–' if none defined)
    (p.file.etags ? p.file.etags.join(' ') : '–'),
    // straight-line distance
    /*
    getDistance(origin, p.location, unit)
      .toLocaleString(moment.locale(), {maximumFractionDigits: 1}) + " " + unit,
    */
    // predicted driving distance and Google Maps Route Planner Link
    (getDrivingDistance(origin, p.location, unit)
      .toLocaleString(moment.locale(), {maximumFractionDigits: 1}) + " " + unit)
      .link('https://www.google.com/maps/dir/?api=1&dir_action=navigate&destination=' + p.location[0] + ',' + p.location[1]),
  ])
);

// show a small legend
dv.paragraph("<small>* Approximate driving distance; when clicked, opens a new Google Maps Route Planner window.</small>");

Changelog

  • 2021-05-08 — Original version.
  • 2021-05-15 — Fixed To-Dos, add YAML features:
    • nearby: <number> <unit> in YAML frontmatter now allows specifying how near it shall be, and the units to display. Knows m, km, mi and yd.
    • location: [<lat>, <lon>] in YAML specifies the starting point (i.e. “here”) for distance calculations.
    • searchterm: … in YAML is a normal Dataview from type search term. Examples:
      searchterm: "#family or #friends or #location"
      
      or
      searchterm: '"People"'
      
    • Make tags inline and clickable. Note: Using my dataview-shorten-links.css snippet will work but display one line per tag. Adapt the CSS to select a.internal-link only, or not to select a.tag if you want more than one tag per line.
    • Code above updated .

Questions, Help

  1. If anyone knows how I can optimize the overuse of getDistance() and getDrivingDistance(), please let me know!

  2. What’s the performace/memory hit creating a new pages variable? I mainly did it to decouple, not creating even longer spaghetti code. Or should I string it all together and not use an extra variable?

  3. If anyone knows about an easy (and working!) way to get at “this” notes YAML frontmatter inside the code block, let me know.

  4. Is it possible to ![[nearby]] embed this within other notes and have it read its YAML frontmatter from the including parent? Because that would be truly dynamic—only keep it in one place and just include it! Would save the hassle of changing it in hundreds of existing people/location notes, eventually!

Have fun & Keep ’em coming!

14 Likes