Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions ssg/content/news/hello-world.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,13 @@ title = 'Launch day'
Ryan and I are very proud to announce the launch of this website sharing the findings from our months of hard work. You can read more about our technical journey on our personal blogs: [Ryan](https://ryan.berge.rs/posts/total-viewshed-algorithm/) and [Tom](https://tombh.co.uk/longest-line-of-sight).

We have lots more plans for the project, so watch this space! Subscribe to our [RSS feed](https://alltheviews.world/index.xml).

## Commentary from around the web
* [Hacker News](https://news.ycombinator.com/item?id=46943568)
* [Lobster.rs](https://lobste.rs/s/8959u3)
* [r/rust](https://www.reddit.com/r/rust/comments/1r00jsi/algorithmically_finding_the_longest_line_of_sight)
* [r/geography](https://www.reddit.com/r/geography/comments/1r01raa/algorithmically_finding_the_longest_line_of_sight/)
* [r/gis](https://www.reddit.com/r/gis/comments/1r00l6e/algorithmically_finding_the_longest_line_of_sight/)
* [r/FromAfar](https://www.reddit.com/r/FromAfar/comments/1r07aka/every_longest_line_of_sight_on_earth/)
* [r/amateurradio](https://www.reddit.com/r/amateurradio/comments/1r09x0c/longest_linesofsight_in_the_world/)

5 changes: 2 additions & 3 deletions ssg/themes/hugo-trainsh/assets/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -479,11 +479,9 @@ body > header {
/* blog post list - clean grid */
ul.blog-posts {
list-style-type: none;
display: inline-block;
padding: 0;
margin: 0;
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 2em 3em;
}

@media (max-width: 560px) {
Expand All @@ -495,6 +493,7 @@ ul.blog-posts {

ul.blog-posts li {
position: relative;
margin-bottom: 2em;
}

ul.blog-posts li::before {
Expand Down
29 changes: 29 additions & 0 deletions website/src/lib/getLongestLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
CDN_BUCKET,
clamp,
endLoadingSpinner,
getElevationAtCoord,
Log,
startLoadingSpinner,
VERSION,
Expand All @@ -22,6 +23,7 @@ export type LongestLine = {
angle: number;
from: LngLat;
to: LngLat;
googleEarth: string;
};

type IndexedTile = {
Expand Down Expand Up @@ -123,6 +125,33 @@ async function getLongestLineCandidate(cog: GeoTIFFImage, coordinate: LngLat) {
return { distance, angle } as LongestLine;
}

export async function makeGoogleEarthLink(line: LongestLine) {
const base = 'https://earth.google.com/web/@';
const latlon = `${line.from.lat},${line.from.lng}`;

// Add a bit because sometimes we're placed under the terrain.
const extraAltitude = 10;

const altitude = (await getElevationAtCoord(line.from)) + extraAltitude;

// We need a little bit of distance so that the `heading` has something to point at.
const distance = 1;

// Google Earth measures heading from North. TODO: we should too.
const heading = 90 - line.angle;

// How wide the camera view is?
const fieldOfView = 15;

// Whether camera is pointing up or down. 90 is just flat looking at the horizon.
const tilt = 90;

// Looks like "CgRCAggBQgIIAEoNCP___________wEQAA", I think it's just user-specific session stuff?
const data = ``;

return `${base}${latlon},${altitude}a,${distance}d,${fieldOfView}y,${heading}h,${tilt}t,0r/data=${data}`;
}

async function getPointFromRaster(cog: GeoTIFFImage, x: number, y: number) {
// We need to find the longest line in a 5x5 grid to get around precision loss.
const around = 2;
Expand Down
27 changes: 15 additions & 12 deletions website/src/lib/renderLongestLine.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { type GeoJSONFeature, type GeoJSONSource, LngLat } from 'maplibre-gl';
import proj4 from 'proj4';
import { navigate } from 'svelte5-router';

import { state } from '../state.svelte.ts';
import { getLongestLine } from './getLongestLine.ts';
import { getLongestLine, makeGoogleEarthLink } from './getLongestLine.ts';

import {
ANGLE_SHIFT,
aeqdProjectionString,
Expand Down Expand Up @@ -58,30 +60,31 @@ function extractCoordFromURL(coordFromURL: string) {
}

export async function render(lngLat: LngLat) {
const longest_line = await getLongestLine(lngLat);
if (longest_line === undefined) {
const longestLine = await getLongestLine(lngLat);
if (longestLine === undefined) {
return;
}

state.longestLine = longest_line;
state.longestLine = longestLine;

if (import.meta.env.DEV) {
console.log(longest_line);
console.log(longestLine);
}

longest_line.angle = longest_line.angle + ANGLE_SHIFT;
longestLine.angle = longestLine.angle + ANGLE_SHIFT;

const θ = toRadians(longest_line.angle);
const dx = longest_line.distance * Math.cos(θ);
const dy = longest_line.distance * Math.sin(θ);
const θ = toRadians(longestLine.angle);
const dx = longestLine.distance * Math.cos(θ);
const dy = longestLine.distance * Math.sin(θ);
const rotatedClockwiseAEQD = rotate(dx, dy, -0.5);
const rotatedAntiAEQD = rotate(dx, dy, +0.5);

const aeqd = aeqdProjectionString(lngLat.lng, lngLat.lat);
const unrotated = proj4(aeqd, proj4.WGS84, [dx, dy]);
longest_line.from = lngLat;
longest_line.to = new LngLat(unrotated[0], unrotated[1]);
state.longestLine = longest_line;
longestLine.from = lngLat;
longestLine.to = new LngLat(unrotated[0], unrotated[1]);
longestLine.googleEarth = await makeGoogleEarthLink(longestLine);
state.longestLine = longestLine;

const rotatedClockwiseLonLat = proj4(aeqd, proj4.WGS84, rotatedClockwiseAEQD);
const rotatedAntiLonLat = proj4(
Expand Down
8 changes: 8 additions & 0 deletions website/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,11 @@ export function setVectorVisibility(state: AppState, isVisible: boolean) {
);
}
}

export async function getElevationAtCoord(coord: LngLat) {
const base = 'https://api.elevationapi.com/api/Elevation';
const url = `${base}?lat=${coord.lat}&lon=${coord.lng}&dataSet=NASADEM`;
const result = await fetch(url);
const payload = await result.json();
return payload.geoPoints[0].elevation;
}
22 changes: 21 additions & 1 deletion website/src/modals/CurrentLine.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { DraftingCompass } from '@lucide/svelte';
import { DraftingCompass, Earth } from '@lucide/svelte';
import CollapsableModal from '../components/CollapsableModal.svelte';
import { lonLatRound } from '../lib/utils';
import { state } from '../state.svelte';
Expand All @@ -22,6 +22,17 @@
To: {lonLatRound(state.longestLine.to)}
</div>
</div>

<div id="google_earth_link">
<Earth />
<a
href={state.longestLine.googleEarth}
title="View on Google Earth"
target="_blank"
>
View on Google Earth ↗
</a>
</div>
{/if}
</CollapsableModal>

Expand All @@ -30,4 +41,13 @@
font-family: monospace;
flex: 0 0 auto;
}

#google_earth_link {
margin-top: 1em;
display: flex;
align-items: center;
a {
margin-left: 0.5em;
}
}
</style>
Loading