I'm trying to draw a path while on my skateboard. The path is not very accurate and sometimes speed will show 2500mph which obviously isn't correct.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
rel="apple-touch-icon"
sizes="180x180"
href="/icons/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/icons/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/icons/favicon-16x16.png"
/>
<link rel="manifest" href="/manifest.json" />
<link rel="mask-icon" href="/icons/safari-pinned-tab.svg" color="#5bbad5" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<title>Tracking Map - Skatespot.com</title>
<script src="https://cdn.jsdelivr.net/npm/#turf/turf#6/turf.min.js"></script>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css"
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
crossorigin=""
/>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css"
/>
<style>
* {
box-sizing: border-box;
}
html {
font-size: 62.5%;
}
body {
margin: 0;
padding: 1rem;
font-size: 1.6rem;
font-family: sans-serif;
width: calc(100vw - 2rem);
height: calc(100vh - 13.7rem);
margin: 0 auto;
}
header {
padding: 1rem 0;
}
#map {
width: 100%;
height: 100%;
}
.marker img {
width: 3rem;
}
.marker span {
display: block;
background: #fff;
width: 10rem;
padding: 1rem;
border-radius: 0.4rem;
border: 1px solid black;
}
#status {
display: block;
}
header nav {
display: flex;
justify-content: flex-start;
align-items: center;
}
header nav * {
margin: 0 0.5rem;
}
#logo {
width: 3rem;
border: none;
display: inline-block;
}
#media (min-width: 481px) {
/* portrait e-readers (Nook/Kindle), smaller tablets # 600 or # 640 wide. */
#status {
display: inline-block;
}
body {
height: calc(100vh - 9.8rem);
}
}
</style>
</head>
<body>
<header>
<nav>
<img id="logo" src="/logo.svg" alt="Skatespot.com" />
<button id="start">start</button>
<button id="stop">stop</button>
<button id="marker">add marker</button>
<button id="export">export</button>
</nav>
<span id="status"></span>
<span id="distance"></span>
</header>
<div id="map"></div>
<script
src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js"
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
crossorigin=""
></script>
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
<script>
const exportButton = document.getElementById("export");
let intv;
let watchId;
const status = document.getElementById("status");
const distance = document.getElementById("distance");
let startTime;
let markers = [];
let stats = { topSpeed: 0 };
const map = L.map("map", {
center: [9.082, 8.6753],
zoom: 8,
});
const osm = L.tileLayer(
"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
{
attribution:
'© SkateSpot, Inc.',
}
).addTo(map);
const mqi = L.tileLayer(
"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
{
id: "mapbox.streets",
attribution:
'© SkateSpot, Inc.',
}
);
const baseMaps = {
Street: osm,
Satellite: mqi,
};
const overlays = {
//add any overlays here
};
L.control
.layers(baseMaps, overlays, { position: "bottomleft" })
.addTo(map);
L.Control.geocoder().addTo(map);
if (!navigator.geolocation) {
console.log("Your browser doesn't support geolocation feature!");
} else {
navigator.geolocation.getCurrentPosition(setMapStart);
}
const trackingPath = [];
const polyline = L.polyline([], {
color: "red",
weight: 3,
className: "path",
}).addTo(map);
function start() {
startTime = new Date().getTime();
watchId = navigator.geolocation.watchPosition(
getPosition,
console.error,
{ enableHighAccuracy: true }
);
}
function setMapStart(position) {
const { latitude, longitude, accuracy } = position.coords;
const pos = [latitude, longitude];
console.log("set: ", pos);
map.setView(new L.LatLng(latitude, longitude), 15, {
animation: true,
});
}
function getPosition(position) {
// console.log(position)
const { latitude, longitude, accuracy } = position.coords;
const pos = [latitude, longitude];
//const pos = turf.randomPosition([49, 23, 43, 20])
trackingPath.push({
pos,
time: new Date().getTime(),
});
console.log("Time", new Date().getTime());
console.log("Time", new Date());
polyline.addLatLng(pos);
map.fitBounds(polyline.getBounds());
//distance
if (trackingPath.length) {
const line = turf.lineString(trackingPath.map((i) => i.pos));
console.log("");
const length = turf.length(line, { units: "miles" });
//speed
curTime = new Date().getTime();
const diffTime = (curTime - startTime) / 1000 / 60 / 60;
const speed = length / diffTime;
const segStop = trackingPath[trackingPath.length - 1];
const segStart = trackingPath[trackingPath.length - 2];
const segment = turf.lineString([segStart.pos, segStop.pos]);
const segmentLength = turf.length(segment, { units: "miles" });
const tDiff = (segStop.time - segStart.time) / 1000 / 60 / 60;
const segmentSpeed = segmentLength / tDiff;
const sSpeed10 = getSegmentSpeed(10, trackingPath);
console.log("speed", speed, length / speed);
stats.distance = parseFloat(length.toFixed(4));
stats.avgSpeed = parseFloat(speed.toFixed(2));
stats.currSpeed = parseFloat(segmentSpeed.toFixed(2));
stats.avgLast10Speed = parseFloat(sSpeed10.toFixed(2));
if (stats.topSpeed <= stats.currSpeed) {
stats.topSpeed = stats.currSpeed;
}
distance.innerHTML = `${stats.distance} miles, avg ${stats.avgSpeed} mph,
segment speed ${stats.currSpeed} and for the last 10 points: ${stats.avgLast10Speed} top speed: ${stats.topSpeed}`;
}
console.log(
"Your coordinate is: Lat: " +
latitude +
" Long: " +
longitude +
" Accuracy: " +
accuracy
);
}
exportButton.addEventListener("click", function () {
console.log("trackingPath", trackingPath);
save(
"data.json",
JSON.stringify({ paths: trackingPath, markers, stats }, null, 2)
);
});
function getSegmentSpeed(segmentPoints, trackingPath) {
if (trackingPath.length < segmentPoints) return 0;
const segStop = trackingPath[trackingPath.length - 1];
const segStart = trackingPath[trackingPath.length - segmentPoints];
const segment = turf.lineString([segStart.pos, segStop.pos]);
const segmentLength = turf.length(segment, { units: "miles" });
const tDiff = (segStop.time - segStart.time) / 1000 / 60 / 60;
const segmentSpeed = segmentLength / tDiff;
return segmentSpeed;
}
function save(filename, data) {
const blob = new Blob([data], { type: "text/csv" });
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveBlob(blob, filename);
} else {
const elem = window.document.createElement("a");
elem.href = window.URL.createObjectURL(blob);
elem.download = filename;
document.body.appendChild(elem);
elem.click();
document.body.removeChild(elem);
}
}
function addMarker(e) {
e.preventDefault();
status.innerText = "adding marker...";
navigator.geolocation.getCurrentPosition((position) => {
const { latitude, longitude, accuracy } = position.coords;
const pos = [latitude, longitude];
markers.push({ pos, time: new Date().getTime() });
const marker = L.marker(pos, {
icon: new L.DivIcon({
className: "marker",
html: `<img src="/icons/marker.svg" alt="" />
<textarea>click to edit</textarea>`,
}),
}).addTo(map);
const Overlaymaps = {
Marker: marker,
};
L.control.layers(Overlaymaps).addTo(map);
status.innerText = `Marker added at ${pos.join(", ")}`;
});
}
function startTracking(e) {
status.innerText = "Started tracking...";
e.preventDefault();
start();
}
function stopTracking(e) {
status.innerText = "Stopped tracking.";
e.preventDefault();
//clearInterval(intv);
navigator.geolocation.clearWatch(watchId);
}
document.getElementById("start").addEventListener("click", startTracking);
document.getElementById("stop").addEventListener("click", stopTracking);
document.getElementById("marker").addEventListener("click", addMarker);
</script>
</body>
</html>
As far as reproducing you'd have to get on a bike or skateboard to test it. But I get long straight lines that appear to show up near beginning and end of a run.
You can test it online here: https://skatespot.profullstack.com/
Click "start" then skate somewhere
Appears to work flawlessly in a car but slower moving transportion seems buggy at best for paths.
Related
I have an API list and I want my divs to float within this huge container. this is my reference - but for some reason this doesn't work for mine (http://jsfiddle.net/bsd3b0r0/1/) I know I'm probably missing something, naming of my divs or something but I simply cannot figure it out. Would appreciate any advice.
/* globals require */
console.log("Hello, Airtable");
let wrapper = document.querySelector(".container");
// load the airtable library, call it "Airtable"
let Airtable = require("airtable");
console.log(Airtable);
// use the airtable library, connect to our base using API key
let base = new Airtable({ apiKey: "keyXfgdNIcv7dW63l" }).base(
"appWiQM0htfVBj9RL"
);
//get the "books" table from the base, select ALL the records, and specify the functions that will receive the data
base("nature_sounds").select({}).eachPage(gotPageOfSounds, gotAllSounds);
// an empty array to hold our book data
let sounds = [];
// callback function that receives our data
function gotPageOfSounds(records, fetchNextPage) {
console.log("gotPageOfSounds()");
// add the records from this page to our books array
sounds.push(...records);
// request more pages
fetchNextPage();
}
// call back function that is called when all pages are loaded
function gotAllSounds(err) {
console.log("gotAllSounds()");
// report an error, you'd want to do something better than this in production
if (err) {
console.log("error loading sounds");
console.error(err);
return;
}
// call functions to log and show the books
consoleLogSounds();
showSounds();
}
// just loop through the books and console.log them
function consoleLogSounds() {
console.log("consoleLogSounds()");
sounds.forEach((sound) => {
console.log("Sound:", sound);
});
}
// loop through the books, create an h2 for each one, and add it to the page
function showSounds() {
console.log("showSounds()");
sounds.forEach((sound) => {
let soundTextHolder = document.createElement("div"); // victoria u changed it from h2 to div dont forget//
soundTextHolder.classList.add("entry");
soundTextHolder.innerText = sound.fields.emotion;
wrapper.appendChild(soundTextHolder);
let soundColor = sound.fields.color_hex_code;
soundTextHolder.style.backgroundColor = soundColor;
let audioHolder = document.createElement("audio");
audioHolder.src = sound.fields.audio_file[0].url;
audioHolder.classList.add("soundClass");
audioHolder.controls = true;
audioHolder.autoplay = true;
audioHolder.loop = true;
soundTextHolder.appendChild(audioHolder);
});
}
// trying to aniamte//
var divsize = 5,
divcount = 50;
var gRows = Math.floor($(".container").innerWidth() / divsize);
var gCols = Math.floor($('.container').innerHeight() / divsize);
var vals = _.shuffle(_.range(divcount));
var xpos = _.shuffle(_.range(gRows));
var ypos = _.shuffle(_.range(gCols));
_.each(vals, function(d, i) {
var $newdiv = $('<div/>').addClass("div");
$newdiv.css({
'position': 'absolute',
'left': (xpos[i] * divsize) + 'px',
'top': (ypos[i] * divsize) + 'px'
}).appendTo('.container').html(d);
animateDiv();
});
function newPosition() {
// Get viewport dimensions (remove the dimension of the div)
var h = $('.container').height() - 50;
var w = $('.container').width() - 50;
var newh = Math.floor(Math.random() * h);
var neww = Math.floor(Math.random() * w);
return [newh, neww];
}
function animateDiv() {
var newp = newPosition();
var oldp = $('div').offset();
var speed = 3000;
$('div').animate({
top: newp[0],
left: newp[1]
}, speed, function() {
animateDiv();
});
};
// // // collect all the divs
// var divs = document.getElementsByTagName('h2');
// // // get window width and height
// / var winWidth = window.innerWidth;
// / var winHeight = window.innerHeight;
// // // i stands for "index". you could also call this banana or haircut. it's a variable
// // for ( var i=0; i < divs.length; i++ ) {
// // // shortcut! the current div in the list
// // var thisDiv = divs[i];
// // // get random numbers for each element
// // randomTop = getRandomNumber(0, winHeight);
// // randomLeft = getRandomNumber(0, winWidth);
// // // update top and left position
// / thisDiv.style.top = randomTop +"px";
// / thisDiv.style.left = randomLeft +"px";
// }
// // function that returns a random number between a min and max
// function getRandomNumber(min, max) {
// return Math.random() * (max - min) + min;
// }
body{
font-family: Space Mono;
background-color: white;
}
h1{
text-align: right;
color: black;
font-family: sans-serif;
}
.container{
width: 100%;
/* align-items: center;
display: grid;
grid-row: inherit;
position: relative; */
}
div{
border-radius: 50% 20% / 10% 40%;
/* border-radius: 10% / 50%; */
width: 30vw;
margin-left: auto;
margin-right: auto;
text-align: center;
/* display: flex; */
flex-direction: column;
box-shadow: inset 0 0 2000px rgba(255, 255, 255, .5);
filter: blur(1px);
/* animation-name: floating;
animation-duration: 3s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out; */
}
/* #keyframes floating {
0% { transform: translate(0, 0px); }
50% { transform: translate(0, 15px); }
100% { transform: translate(0, -0px); }
} */
.entry1{
width: 60vw;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EllasticCollection</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Mono&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>sound and color through emotion</h1>
<div id="myData" class="container">
<div class="individual-colors"></div>
</div>
<script src="airtable.browser.js" defer></script>
<script src="script.js" defer></script>
</body>
</html>
I am playing around with intersection observer to create an infinite scroll dog website. As you scroll and 6 dogs appear, an api fires off 6 more times to grab more dogs to add to the DOM. I would like for the dogs to load in as a user scrolls but as an already viewed dog leaves the viewport and goes up on the page, that element is then deleted off the page. SO the dogs always load in scrolling down, but scrolling up you are always at the top of the page. My current implementation in the function called lastFunc is causing it to act really weird. How can I achieve the desired effect.
class CardGenerator {
constructor() {
this.$cardContainer = document.querySelector('.card-container');
this.$allCards = undefined;
this.observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
entry.target.classList.toggle('show', entry.isIntersecting);
if (entry.isIntersecting) {
this.observer.unobserve(entry.target);
}
});
},
{
threshold: 1,
rootMargin: '150px',
}
);
this.loadNewCards();
}
cacheDOMElements() {
this.$allCards = document.querySelectorAll('.card');
}
loadNewCards() {
for (let index = 0; index < 6; index++) {
fetch('https://dog.ceo/api/breeds/image/random', { method: 'GET' })
.then((result) => {
return result.json();
})
.then((r) => {
console.log(r);
const card = document.createElement('div');
card.classList.add('card');
const imageElement = document.createElement('img');
imageElement.classList.add('forza-img');
imageElement.setAttribute('src', r.message);
card.appendChild(imageElement);
this.observer.observe(card);
this.$cardContainer.append(card);
this.cacheDOMElements();
if (this.$allCards.length % 6 === 0) this.lastFunc();
});
}
}
lastFunc() {
console.log(this.$allCards);
if (this.$allCards.length > 12) {
this.$allCards.forEach((item, idx) => {
if (idx < 6) {
item.remove();
}
});
}
this.$allCards.forEach((card, idx) => {
this.observer.observe(card);
});
const lastCardObserver = new IntersectionObserver((entries) => {
const $lastCard = entries[0];
if (!$lastCard.isIntersecting) return;
this.loadNewCards();
lastCardObserver.unobserve($lastCard.target);
});
lastCardObserver.observe(document.querySelector('.card:last-child'));
}
}
const cardGenerator = new CardGenerator();
html,
body {
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
}
.card {
float: left;
width: 48vw;
margin: 1%;
transform: translateX(100px);
opacity: 0;
transition: 150ms;
}
.card.show {
transform: translateY(0);
opacity: 1;
}
.card img {
width: 100%;
border-radius: 15px;
height: 30vh;
object-fit: cover;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Dog Random Images</h1>
<div class="card-container"></div>
</body>
<script src="app.js" ></script>
</html>
When deleting elements, the entire contents of the container are shifted and observers start to fire. In order for observers not to be triggered when deleting an element, it is necessary to shift the scroll just before deleting the element to the height of this element.
Example below:
class CardGenerator {
constructor() {
this.$cardContainer = document.querySelector('.card-container');
this.$allCards = undefined;
this.observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
entry.target.classList.add('show', entry.isIntersecting);
if (entry.isIntersecting) {
this.observer.unobserve(entry.target);
}
});
},
{
threshold: 1,
rootMargin: '150px',
}
);
this.loadNewCards();
}
cacheDOMElements() {
this.$allCards = document.querySelectorAll('.card');
}
loadNewCards() {
for (let index = 0; index < 6; index++) {
fetch('https://dog.ceo/api/breeds/image/random', { method: 'GET' })
.then((result) => {
return result.json();
})
.then((r) => {
console.log(r);
const card = document.createElement('div');
card.classList.add('card');
const imageElement = document.createElement('img');
imageElement.classList.add('forza-img');
imageElement.setAttribute('src', r.message);
card.appendChild(imageElement);
this.observer.observe(card);
this.$cardContainer.append(card);
this.cacheDOMElements();
if (this.$allCards.length % 6 === 0) this.lastFunc();
});
}
}
lastFunc() {
console.log(this.$allCards);
if (this.$allCards.length > 12) {
this.$allCards.forEach((item, idx) => {
if (idx < 6) {
const scrollTop = this.$cardContainer.scrollTop;
const height = item.offsetHeight;
this.$cardContainer.scrollTo(0, Math.max(0, scrollTop - height));
item.remove();
}
});
}
this.$allCards.forEach((card, idx) => {
this.observer.observe(card);
});
const lastCardObserver = new IntersectionObserver((entries) => {
const $lastCard = entries[0];
if (!$lastCard.isIntersecting) return;
this.loadNewCards();
lastCardObserver.unobserve($lastCard.target);
});
lastCardObserver.observe(document.querySelector('.card:last-child'));
}
}
const cardGenerator = new CardGenerator();
html,
body {
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
}
.card {
float: left;
width: 48vw;
margin: 1%;
transform: translateX(100px);
opacity: 0;
transition: 150ms;
}
.card.show {
transform: translateY(0);
opacity: 1;
}
.card img {
width: 100%;
border-radius: 15px;
height: 30vh;
object-fit: cover;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Dog Random Images</h1>
<div class="card-container"></div>
</body>
<script src="app.js" ></script>
</html>
I hope this will help you somehow.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<style>
body {
height: 100%;
width: 100%;
box-sizing: border-box;
padding: 0;
margin: 0;
}
.card {
width: 48vw;
margin: 1%;
}
.card.show {
// opacity: 1;
}
.card img {
width: 100%;
border-radius: 15px;
height: 30vh;
object-fit: cover;
}
.card-container{
border: solid 1px #00f;
padding: 20px;
overflow-y:scroll;
}
</style>
<style>
#sentinel{
height:0px;
}
</style>
<h1>Dog Random Images</h1>
<div class="card-container">
<div id="sentinel"></div>
</div>
<script>
/* Question on https://stackoverflow.com/questions/70482606/delete-elements-that-have-intersected-the-viewport */
class CardGenerator {
constructor() {
this.$cardContainer = document.querySelector('.card-container');
this.$allCards = undefined;
this.mysentinel = document.querySelector('#sentinel');
this.observer = new IntersectionObserver(
(entries) => {
let [entry] = entries; //destructure array, get first entry - should only be 1 - sentinel
if (entry.isIntersecting) {
this.observer.unobserve(entry.target);
this.loadNewCards();
}
}, {
threshold: 1,
rootMargin: '150px' /*expanded root/viewport(due to null) by 150px*/,
}
);
this.loadNewCards();
} // end constructor;
cacheDOMElements() {
//The Document method querySelectorAll() returns a static (not live) NodeList
this.$allCards = document.querySelectorAll('.card');
}
loadNewCards() {
/* https://stackoverflow.com/questions/31710768/how-can-i-fetch-an-array-of-urls-with-promise-all , from peirix*/
this.mypromises = [];
this.mymessages = [];
this.urls = new Array(6).fill("https://dog.ceo/api/breeds/image/random", 0, 6);
//create array of promises
var promises = this.urls.map(url => fetch(url).then(y => y.json()));
//Promise.all() method takes an iterable of promises
//promise.all returns a single Promise that resolves to an array of the results of the input promises
Promise.all(promises)
.then(results => {
//accumulate all the urls from message property
results.forEach(v => this.mymessages.push(v.message));
})
.finally(() => {
let idx = 0;
for (let message of this.mymessages) {
const card = document.createElement('div');
card.classList.add('card');
const imageElement = document.createElement('img');
imageElement.setAttribute('src', message);
imageElement.setAttribute('title', `${idx++}:${message}`);
card.appendChild(imageElement);
this.$cardContainer.appendChild(card);
}// end for
this.cacheDOMElements();
//stop this sentinel possibly hitting the observer to loadnewcards as we (re)move cards
this.observer.unobserve(this.mysentinel);
//if number of cards is>12 then takeoff the first 6
if (this.$allCards.length > 12) {
for (let i = 0; i < 6; i++) {
this.$allCards[i].remove();
}
}
//already exists so move it to bottom of container div
this.$cardContainer.appendChild(this.mysentinel);
/*this should be outside the root so when it invokes observer it will not fire loadnewcards*/
this.observer.observe(this.mysentinel);
}); //end of finally end of Promise.all
} //end loadnewcards
} //class CardGenerator
const cardGenerator = new CardGenerator();
</script>
</body>
</html>
Good day!
I am trying to play around with animating a pulsating radius based on leaflet L.cicle and have managed to limit the radius size.
However, I am having a problem with several iterations of the radius are not removed from the map, resulting in a large number of circles.
I would appreciate some advice on how to remove the previous iterations of the circle and keep the most recent circle generated.
<!DOCTYPE html>
<html>
<head>
<title>Map</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" type="image/x-icon" href="docs/images/favicon.ico" />
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/>
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
<style>
html, body {
height: 100%;
margin: 0;
}
#map {
width: 600px;
height: 400px;
}
</style>
<style>body { padding: 0; margin: 0; } #map { height: 100%; width: 100vw; }</style>
</head>
<body>
<div></div>
<div id='map'></div>
<script>
var map = L.map('map').setView([1.3400776203517657, 103.88408580637439],6);
L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=ACCESS_TOKEN', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'Imagery © Mapbox',
id: 'mapbox/streets-v11',
tileSize: 512,
zoomOffset: -1
}).addTo(map);
function radiuspulse() {
const secs = 0.1 * 60;
let radiuschange = 0;
setInterval(function() {
if (radiuschange < 50000) {
radiuschange += 500;
const circle = L.circle([1.3400776203517657, 103.88408580637439], {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.1,
weight:0,
id: 'abc123',
radius: radiuschange
}).addTo(map);
function clearcontent() {
document.getElementsByTagName("g").innerHTML = "";
};
}
else {
// clearInterval();
let radiuschange = 0;
radiuspulse();
}
}, secs);
}
radiuspulse()
</script>
</body>
</html>
Don't create always a new Circle, change only the radius with setRadius():
Also you can remove the circle with circle.removeFrom(map)
var circle = L.circle([1.3400776203517657, 103.88408580637439], {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.1,
weight:0,
id: 'abc123',
radius: 500
}).addTo(map);
var interval = null;
function radiuspulse() {
const secs = 0.1 * 60;
let radiuschange = circle.getRadius();
interval = setInterval(function() {
if (radiuschange < 50000) {
radiuschange += 500;
circle.setRadius(radiuschange);
}
else {
clearInterval(interval);
circle.setRadius(0);
radiuspulse();
}
}, secs);
}
function clearcontent() {
circle.removeFrom(map);
}
The dragging works but the bug is that when I drag the element somehow "jumps" and does not flow with the mouse. See it in the code. Don't worry about removing event listeners, I will add them as soon as this works.
I have an issue on a draggable "div" element. I've searched many answers before I posted this question but nothing seems to be the solution(or maybe I am not understanding the problem really well).
Thank you!
const hotspot = document.getElementsByClassName("hotspot")[0];
const container = document.getElementsByClassName("container")[0];
let containerRect = container.getBoundingClientRect();
let hsRect = hotspot.getBoundingClientRect();
let relMouse = { x: 0, y: 0 };
let windowMouse = { x: 0, y: 0 };
let isUserIntercating = false;
const handlePointerUp = (e) => {
isUserIntercating = false;
};
const handlePointerDown = (e) => {
isUserIntercating = true;
hsRect = hotspot.getBoundingClientRect();
relMouse = { x: e.pageX - hsRect.x, y: e.pageY - hsRect.y };
window.addEventListener("pointerup", handlePointerUp, false);
};
const handlePointerMove = (e) => {
hsRect = hotspot.getBoundingClientRect();
containerRect = container.getBoundingClientRect();
windowMouse = { x: e.clientX - relMouse.x, y: e.clientY - relMouse.y };
};
const update = (t) => {
requestAnimationFrame(update);
if (isUserIntercating) {
hotspot.style.transform = `translate(${
windowMouse.x - containerRect.x
}px,0px)`;
}
};
update();
hotspot.addEventListener("pointerdown", handlePointerDown, false);
window.addEventListener("pointermove", handlePointerMove, false);
body {
font-family: sans-serif;
margin: 0;
}
.container {
padding: 0;
margin: 50px;
max-width: 600px;
min-height: 600px;
background-color: blanchedalmond;
}
.hotspot {
/* position: absolute; */
background-color: aqua;
/* transform: translate(100px, 100px); */
min-height: 100px;
max-width: 100px;
z-index: 200;
}
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div class="container">
<div class="hotspot"></div>
</div>
<script src="src/index.js"></script>
</body>
</html>
Try like this:
const hotspot = document.getElementsByClassName("hotspot")[0];
const container = document.getElementsByClassName("container")[0];
let containerRect = container.getBoundingClientRect();
let hsRect = hotspot.getBoundingClientRect();
let relMouse = { x: 0, y: 0 };
let windowMouse = { x: 0, y: 0 };
let isUserIntercating = false;
const handlePointerUp = (e) => {
isUserIntercating = false;
};
const handlePointerDown = (e) => {
isUserIntercating = true;
hsRect = hotspot.getBoundingClientRect();
relMouse = { x: e.pageX - hsRect.x, y: e.pageY - hsRect.y };
window.addEventListener("pointerup", handlePointerUp, false);
};
const handlePointerMove = (e) => {
hsRect = hotspot.getBoundingClientRect();
containerRect = container.getBoundingClientRect();
windowMouse = { x: e.clientX - relMouse.x, y: e.clientY - relMouse.y };
requestAnimationFrame(update);
};
const update = (t) => {
if (isUserIntercating) {
hotspot.style.transform = `translate(${
windowMouse.x - containerRect.x
}px,0px)`;
}
};
hotspot.addEventListener("pointerdown", handlePointerDown, false);
window.addEventListener("pointermove", handlePointerMove, false);
body {
font-family: sans-serif;
margin: 0;
}
.container {
padding: 0;
margin: 50px;
max-width: 600px;
min-height: 600px;
background-color: blanchedalmond;
}
.hotspot {
/* position: absolute; */
background-color: aqua;
/* transform: translate(100px, 100px); */
min-height: 100px;
max-width: 100px;
z-index: 200;
}
<!DOCTYPE html>
<html>
<head>
<title>Parcel Sandbox</title>
<meta charset="UTF-8" />
</head>
<body>
<div class="container">
<div class="hotspot"></div>
</div>
<script src="src/index.js"></script>
</body>
</html>
The issue seemed to be that the update function kept calling itself all the time, which is not really ideal. The update should only be called in the handlePointerMove function (only change the hotspot position when the mouse moves).
I'm making tile map with two layers: map from my files stored on HDD, second is OSM. I've made it with gdal2tiles.py
How it looks: Two layers using OpenLayers
I want to add a onclick popup which will show link to the original tile in bigger resolution. My idea is to attach a .csv file with following structure:
[latitude],[longitude],[image index]
My function in python parses this file and gets image index. Here is it:
def find_pic(lat, lng):
my_file = open('log wynikowy epsg 3857.csv', 'r')
list_of_lines = my_file.readlines()
print len(list_of_lines)
for line in list_of_lines:
if (line.rstrip()).split(",")[0] == lat and (line.rstrip()).split(',')[1] == lng:
return line.rstrip().split(',')[2]
Then I tried to rewrite it in JavaScript:
function find_pic(lat, lng) {
fh = fopen(getScriptPath('log wynikowy epsg 3857.csv'), 0);
fh_length = flength(fh);
var allText = fread(fh, fh_length);
var fldData = [];
for (x = 0; x < allTextLines.length; x++) {
fldData = allTextLines[x].split(',');
}
for (i = 0; i < fldData.length.length; i++) {
if (allTextLines[i].split(',')[0] == lat && allTextLines[i].split(',')[1] == lng) {
var result = allTextLines[i].split(',')[2];
}
}
var pic_link = "/data/" + result + ".jpg";
return pic_link;
}
In my html file, I copy-paste popup with location example and try to implement my function. Here is the whole HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
<head>
<title>Ortofotomapa</title>
<meta http-equiv='imagetoolbar' content='no'/>
<style type="text/css"> v\:* {behavior:url(#default#VML);}
html, body { overflow: hidden; padding: 0; height: 100%; width: 100%; font-family: 'Lucida Grande',Geneva,Arial,Verdana,sans-serif; }
body { margin: 10px; background: black; }
h1 { margin: 0; padding: 6px; border:0; font-size: 20pt; }
#header { height: 43px; padding: 0; background-color: #eee; border: 1px solid #888; }
#subheader { height: 12px; text-align: right; font-size: 10px; color: #555;}
#map { height: 95%; border: 1px solid #888; }
.olImageLoadError { display: none; }
.olControlLayerSwitcher .layersDiv { border-radius: 10px 0 0 10px; }
</style>
<script src='http://maps.google.com/maps/api/js?sensor=false&v=3.7'></script>
<script src="http://www.openlayers.org/api/2.12/OpenLayers.js"></script>
<script>
var map;
var mapBounds = new OpenLayers.Bounds( 20.9278783732, 52.0107452018, 20.943887533, 52.0184497743);
var mapMinZoom = 14;
var mapMaxZoom = 20;
var emptyTileURL = "http://www.maptiler.org/img/none.png";
OpenLayers.IMAGE_RELOAD_ATTEMPTS = 3;
function init(){
var options = {
div: "map",
controls: [],
projection: "EPSG:900913",
displayProjection: new OpenLayers.Projection("EPSG:4326"),
numZoomLevels: 20
};
map = new OpenLayers.Map(options);
// Create OSM layer
var osm = new OpenLayers.Layer.OSM("OpenStreetMap");
// create TMS Overlay layer
var tmsoverlay = new OpenLayers.Layer.TMS("TMS Overlay", "",
{
serviceVersion: '.',
layername: '.',
alpha: true,
type: 'png',
isBaseLayer: false,
getURL: getURL
});
if (OpenLayers.Util.alphaHack() == false) {
tmsoverlay.setOpacity(1.0);
}
map.addLayers([osm, tmsoverlay]);
var switcherControl = new OpenLayers.Control.LayerSwitcher();
map.addControl(switcherControl);
switcherControl.maximizeControl();
map.zoomToExtent(mapBounds.transform(map.displayProjection, map.projection));
map.addControls([//new OpenLayers.Control.PanZoomBar(),
new OpenLayers.Control.Navigation(),
new OpenLayers.Control.MousePosition({
numDigits: 10}),
//new OpenLayers.Control.ArgParser(),
new OpenLayers.Control.Attribution()]);
}
function getURL(bounds) {
bounds = this.adjustBounds(bounds);
var res = this.getServerResolution();
var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));
var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h));
var z = this.getServerZoom();
if (this.map.baseLayer.CLASS_NAME === 'OpenLayers.Layer.Bing') {
z+=1;
}
var path = this.serviceVersion + "/" + this.layername + "/" + z + "/" + x + "/" + y + "." + this.type;
var url = this.url;
if (OpenLayers.Util.isArray(url)) {
url = this.selectUrl(path, url);
}
if (mapBounds.intersectsBounds(bounds) && (z >= mapMinZoom) && (z <= mapMaxZoom)) {
return url + path;
} else {
return emptyTileURL;
}
}
function getWindowHeight() {
if (self.innerHeight) return self.innerHeight;
if (document.documentElement && document.documentElement.clientHeight)
return document.documentElement.clientHeight;
if (document.body) return document.body.clientHeight;
return 0;
}
function getWindowWidth() {
if (self.innerWidth) return self.innerWidth;
if (document.documentElement && document.documentElement.clientWidth)
return document.documentElement.clientWidth;
if (document.body) return document.body.clientWidth;
return 0;
}
function resize() {
var map = document.getElementById("map");
var header = document.getElementById("header");
var subheader = document.getElementById("subheader");
map.style.height = (getWindowHeight()-80) + "px";
map.style.width = (getWindowWidth()-20) + "px";
header.style.width = (getWindowWidth()-20) + "px";
subheader.style.width = (getWindowWidth()-20) + "px";
if (map.updateSize) { map.updateSize(); };
}
function find_pic(lat, lng){
fh = fopen(getScriptPath('log wynikowy epsg 3857.csv'), 0);
fh_length = flength(fh);
var allText = fread(fh, fh_length);
var fldData =[];
for (x=0; x<allTextLines.length; x++){
fldData = allTextLines[x].split(',');
}
for (i=0; i<fldData.length.length; i++){
if (allTextLines[i].split(',')[0] == lat && allTextLines[i].split(',')[1] == lng){
var result = allTextLines[i].split(',')[2];
}
}
var link_pic = "/data/" + result + ".jpg";
return link_pic;
}
var overlay = new ol.Overlay({
element: container,
autoPan: true,
autoPanAnimation: {
duration: 250
}
}));
closer.onclick = function() {
overlay.setPosition(undefined);
closer.blur();
return false;
};
map.on('singleclick', function(evt) {
var coordinate = evt.coordinate;
var link_pic = find_pic(coordinate[0],coordinate[1]);
content.innerHTML = '<p>Link do oryginalnego zdjecia: <a href=link_pic>Klik</a></p>';
overlay.setPosition(coordinate);
});
onresize=function(){ resize(); };
var element = document.getElementById('popup');
</script>
</head>
<body onload="init()">
<div id="header"><h1>Ortofotomapa</h1></div>
<div id="subheader">Generated by MapTiler/GDAL2Tiles, Copyright © 2008 Klokan Petr Pridal, GDAL & OSGeo GSoC
<!-- PLEASE, LET THIS NOTE ABOUT AUTHOR AND PROJECT SOMEWHERE ON YOUR WEBSITE, OR AT LEAST IN THE COMMENT IN HTML. THANK YOU -->
</div>
<div id="map"></div>
<script type="text/javascript" >resize()</script>
</body>
</html>
EDIT:After this, page is all black. I want to have onclick popup, which will contain link to a big resolution photo.