mirror of
https://github.com/garraflavatra/gpstool.git
synced 2025-07-19 22:08:02 +00:00
Initial commit
This commit is contained in:
79
src/app.css
Normal file
79
src/app.css
Normal file
@ -0,0 +1,79 @@
|
||||
html, body {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
* {
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 32px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
ul li, ol li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
dl dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
dl dd {
|
||||
margin: 0 0 12px 0;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #fff;
|
||||
padding: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.card h2 {
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 6px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.input {
|
||||
min-width: 100px;
|
||||
padding: 2px 4px;
|
||||
font: inherit;
|
||||
border: 1px solid #888;
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
*:last-child { margin-bottom: 0; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html, body {
|
||||
background-color: #333;
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
a { color: #ade; }
|
||||
|
||||
.card { background-color: #444; }
|
||||
.card h2 { border-color: #333; }
|
||||
}
|
8
src/app.js
Normal file
8
src/app.js
Normal file
@ -0,0 +1,8 @@
|
||||
import './app.css';
|
||||
import App from './app.svelte';
|
||||
|
||||
const app = new App({
|
||||
target: document.getElementById('app'),
|
||||
});
|
||||
|
||||
export default app;
|
116
src/app.svelte
Normal file
116
src/app.svelte
Normal file
@ -0,0 +1,116 @@
|
||||
<script>
|
||||
/* global L */
|
||||
import { onMount } from 'svelte';
|
||||
import { latLngToDMS, latLngToDecimal, latLngToExif } from './conversion.js';
|
||||
import { getAppleUrl, getDuckDuckGoUrl, getGoogleUrl } from './urls.js';
|
||||
|
||||
let map;
|
||||
let mapDiv;
|
||||
let marker;
|
||||
let coords = L.latLng(51.507, -0.1275);
|
||||
|
||||
function updateMap() {
|
||||
map.setView(coords);
|
||||
marker.setLatLng(coords);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
map = L.map(mapDiv).setView(coords, 9);
|
||||
|
||||
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||||
}).addTo(map);
|
||||
|
||||
L.polyline([ [ -90, 0 ], [ 90, 0 ] ]).addTo(map);
|
||||
L.polyline([ [ 0, -180 ], [ 0, 180 ] ]).addTo(map);
|
||||
|
||||
marker = L.marker(coords).addTo(map);
|
||||
|
||||
map.on('click', event => {
|
||||
coords = event.latlng;
|
||||
updateMap();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<main class="grid">
|
||||
<div class="info">
|
||||
<section class="card">
|
||||
<h2>Coordinates</h2>
|
||||
|
||||
<dl>
|
||||
<dt>Decimal</dt>
|
||||
<dd>{latLngToDecimal(coords)}</dd>
|
||||
|
||||
<dt>DMS</dt>
|
||||
<dd>{latLngToDMS(coords)}</dd>
|
||||
|
||||
<dt>EXIF</dt>
|
||||
<dd>{latLngToExif(coords)}</dd>
|
||||
</dl>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>Go to</h2>
|
||||
<div class="coordinate-input">
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
step={0.25}
|
||||
bind:value={coords.lat}
|
||||
on:change={updateMap}
|
||||
/>
|
||||
<input
|
||||
class="input"
|
||||
type="number"
|
||||
step={0.25}
|
||||
bind:value={coords.lng}
|
||||
on:change={updateMap}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>Open in</h2>
|
||||
<ul>
|
||||
<li><a href={getAppleUrl(coords)} target="_blank">Apple Maps</a></li>
|
||||
<li><a href={getGoogleUrl(coords)} target="_blank">Google Maps</a></li>
|
||||
<li><a href={getDuckDuckGoUrl(coords)} target="_blank">DuckDuckGo</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<p>
|
||||
Star gpstool on
|
||||
<a href="https://github.com/garraflavatra/gpstool" target="_blank" rel="noopener noreferrer">
|
||||
GitHub
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="map" bind:this={mapDiv} />
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template: 1fr / 250px 1fr;
|
||||
height: 100%;
|
||||
gap: 12px;
|
||||
}
|
||||
@media (max-width: 750px) {
|
||||
.grid {
|
||||
grid-template: 1fr 2fr / 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.coordinate-input {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
grid-template: 1fr / 1fr 1fr;
|
||||
}
|
||||
</style>
|
37
src/conversion.js
Normal file
37
src/conversion.js
Normal file
@ -0,0 +1,37 @@
|
||||
export function latLngToDecimal({ lat, lng }) {
|
||||
const precision = 5;
|
||||
return `${lat.toFixed(precision)}; ${lng.toFixed(precision)}`;
|
||||
}
|
||||
|
||||
// DMS = degrees, minutes, seconds
|
||||
function decimalToDMS(coord) {
|
||||
const negative = coord < 0;
|
||||
coord = Math.abs(coord);
|
||||
|
||||
const degrees = Math.trunc(coord);
|
||||
let minutes = (coord - degrees) * 60;
|
||||
const seconds = Math.trunc((minutes - Math.trunc(minutes)) * 60);
|
||||
minutes = Math.trunc(minutes);
|
||||
|
||||
return { degrees, minutes, seconds, negative };
|
||||
}
|
||||
|
||||
export function latLngToDMS({ lat, lng }) {
|
||||
lat = decimalToDMS(lat);
|
||||
lng = decimalToDMS(lng);
|
||||
const latRef = lat.negative ? 'S' : 'N';
|
||||
const lngRef = lng.negative ? 'W' : 'E';
|
||||
|
||||
return `${lat.degrees}°${lat.minutes}'${lat.seconds}"${latRef} ` +
|
||||
`${lng.degrees}°${lng.minutes}'${lng.seconds}"${lngRef}`;
|
||||
}
|
||||
|
||||
export function latLngToExif({ lat, lng }) {
|
||||
lat = decimalToDMS(lat);
|
||||
lng = decimalToDMS(lng);
|
||||
const latRef = lat.negative ? 'S' : 'N';
|
||||
const lngRef = lng.negative ? 'W' : 'E';
|
||||
|
||||
return `${lat.degrees},${lat.minutes}.${lat.seconds}${latRef} ` +
|
||||
`${lng.degrees},${lng.minutes}.${lng.seconds}${lngRef}`;
|
||||
}
|
17
src/urls.js
Normal file
17
src/urls.js
Normal file
@ -0,0 +1,17 @@
|
||||
import { latLngToDMS } from './conversion.js';
|
||||
|
||||
export function getAppleUrl({ lat, lng }) {
|
||||
return `https://maps.apple.com/?address=${lat},${lng}`;
|
||||
}
|
||||
|
||||
export function getGoogleUrl(latlng) {
|
||||
return `https://www.google.com/maps/place/${encodeURIComponent(
|
||||
latLngToDMS(latlng)
|
||||
)}`;
|
||||
}
|
||||
|
||||
export function getDuckDuckGoUrl(latlng) {
|
||||
return `https://duckduckgo.com/?q=${encodeURIComponent(
|
||||
latLngToDMS(latlng)
|
||||
)}&iaxm=maps`;
|
||||
}
|
2
src/vite-env.d.ts
vendored
Normal file
2
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/// <reference types="svelte" />
|
||||
/// <reference types="vite/client" />
|
Reference in New Issue
Block a user