mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
More work on automatic map creation
This commit is contained in:
parent
14679bd7d8
commit
acb55044d1
1
.gitignore
vendored
1
.gitignore
vendored
@ -37,3 +37,4 @@ package-lock.json
|
||||
frontend/server/public/plugins/controltipsplugin/index.js
|
||||
frontend/website/plugins/controltips/index.js
|
||||
/frontend/server/public/maps
|
||||
*.pyc
|
||||
|
||||
@ -23,9 +23,10 @@
|
||||
"regedit": "^5.1.2",
|
||||
"save": "^2.9.0",
|
||||
"sha256": "^0.2.0",
|
||||
"sharp": "^0.33.2",
|
||||
"srtm-elevation": "^2.1.2",
|
||||
"tcp-ping-port": "^1.0.1",
|
||||
"uuid": "^9.0.1",
|
||||
"yargs": "^17.7.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
const express = require('express');
|
||||
const sharp = require('sharp')
|
||||
const fs = require('fs');
|
||||
const pfs = require('fs/promises')
|
||||
const router = express.Router();
|
||||
|
||||
router.get('/theme/*', function (req, res, next) {
|
||||
@ -15,4 +18,126 @@ router.put('/theme/:newTheme', function (req, res, next) {
|
||||
res.end("Ok");
|
||||
});
|
||||
|
||||
router.get('/maps/:map/:z/:x/:y.png', async function (req, res, next) {
|
||||
let map = req.params.map;
|
||||
let x = req.params.x;
|
||||
let y = req.params.y;
|
||||
let z = req.params.z;
|
||||
|
||||
if (fs.existsSync(`.\\public\\maps\\${map}`)) {
|
||||
if (!await renderImage(map, z, x, y, res)) {
|
||||
/* No image was found */
|
||||
res.sendStatus(404);
|
||||
}
|
||||
} else {
|
||||
/* The requested map does not exist */
|
||||
res.sendStatus(404);
|
||||
}
|
||||
});
|
||||
|
||||
async function renderImage(map, z, x, y, res, recursionDepth = 0) {
|
||||
if (recursionDepth == 20) {
|
||||
console.log("Render image, maximum recursion depth reached")
|
||||
/* We have reached limit recusion depth, something went wrong */
|
||||
return false;
|
||||
}
|
||||
/* If the requested image exists, send it straight away */
|
||||
if (fs.existsSync(`.\\public\\maps\\${map}\\${z}\\${x}\\${y}.png`)) {
|
||||
res.sendFile(`${process.cwd()}\\public\\maps\\${map}\\${z}\\${x}\\${y}.png`);
|
||||
return true;
|
||||
} else {
|
||||
/* If the requested image doesn't exist check if there is a "source" tile at a lower zoom level we can split up */
|
||||
let sourceZoom = z - 1;
|
||||
let sourceX = Math.floor(x / 2);
|
||||
let sourceY = Math.floor(y / 2);
|
||||
|
||||
/* Keep decreasing the zoom level until we either find an image or reach maximum zoom out and must return false */
|
||||
while (sourceZoom >= 0) {
|
||||
/* We have found a possible source image */
|
||||
if (fs.existsSync(`.\\public\\maps\\${map}\\${sourceZoom}\\${sourceX}\\${sourceY}.png`)) {
|
||||
/* Split the image into four. We can retry up to 10 times to clear any race condition on the files */
|
||||
let retries = 10;
|
||||
while (!await splitTile(map, sourceZoom, sourceX, sourceY) && retries > 0) {
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
retries--;
|
||||
}
|
||||
/* Check if we exited because we reached the maximum retry value */
|
||||
if (retries != 0) {
|
||||
/* Recursively recall the function now that we have a new "source" image */
|
||||
return await renderImage(map, z, x, y, res, recursionDepth + 1);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
/* Keep searching at a higher level */
|
||||
sourceZoom = sourceZoom - 1;
|
||||
sourceX = Math.floor(sourceX / 2);
|
||||
sourceY = Math.floor(sourceY / 2);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function splitTile(map, z, x, y) {
|
||||
try {
|
||||
/* Load the source image */
|
||||
let img = sharp(`${process.cwd()}\\public\\maps\\${map}\\${z}\\${x}\\${y}.png`);
|
||||
|
||||
/* Create the necessary folders */
|
||||
await pfs.mkdir(`${process.cwd()}\\public\\maps\\${map}\\${z + 1}\\${2*x}`, { recursive: true })
|
||||
await pfs.mkdir(`${process.cwd()}\\public\\maps\\${map}\\${z + 1}\\${2*x + 1}`, { recursive: true })
|
||||
|
||||
/* Split the image into four parts */
|
||||
await resizePromise(img, 0, 0, map, z + 1, 2 * x, 2 * y);
|
||||
img = sharp(`${process.cwd()}\\public\\maps\\${map}\\${z}\\${x}\\${y}.png`);
|
||||
await resizePromise(img, 128, 0, map, z + 1, 2 * x + 1, 2 * y);
|
||||
img = sharp(`${process.cwd()}\\public\\maps\\${map}\\${z}\\${x}\\${y}.png`);
|
||||
await resizePromise(img, 0, 128, map, z + 1, 2 * x, 2 * y + 1);
|
||||
img = sharp(`${process.cwd()}\\public\\maps\\${map}\\${z}\\${x}\\${y}.png`);
|
||||
await resizePromise(img, 128, 128, map, z + 1, 2 * x + 1, 2 * y + 1);
|
||||
return true;
|
||||
} catch (err) {
|
||||
if (err.code !== 'EBUSY') {
|
||||
console.error(err);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns a promise, extracts a 128x128 pixel chunk from an image, resizes it to 256x256, and saves it to file */
|
||||
function resizePromise(img, left, top, map, z, x, y) {
|
||||
return new Promise((res, rej) => {
|
||||
if (fs.existsSync(`${process.cwd()}\\public\\maps\\${map}\\${z}\\${x}\\${y}.png`)) {
|
||||
res(true);
|
||||
}
|
||||
img.extract({ left: left, top: top, width: 128, height: 128 }).resize(256, 256).toFile(`${process.cwd()}\\public\\maps\\${map}\\${z}\\${x}\\${y}.temp.png`, function (err) {
|
||||
if (err) {
|
||||
rej(err);
|
||||
} else {
|
||||
try {
|
||||
fs.renameSync(`${process.cwd()}\\public\\maps\\${map}\\${z}\\${x}\\${y}.temp.png`, `${process.cwd()}\\public\\maps\\${map}\\${z}\\${x}\\${y}.png`);
|
||||
} catch (err) {
|
||||
if (err.code === 'EBUSY') {
|
||||
/* The resource is busy, someone else is writing or renaming it. Reject the promise so we can try again */
|
||||
rej(err);
|
||||
} else if (err.code === 'ENOENT') {
|
||||
/* Someone else renamed the file already so this is not a real error */
|
||||
if (fs.existsSync(`${process.cwd()}\\public\\maps\\${map}\\${z}\\${x}\\${y}.png`)) {
|
||||
res(true);
|
||||
}
|
||||
/* Something odd happened, reject and try again */
|
||||
else {
|
||||
rej(err);
|
||||
}
|
||||
} else {
|
||||
rej(err);
|
||||
}
|
||||
}
|
||||
res(true)
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@ -192,9 +192,9 @@ export const mapLayers = {
|
||||
attribution: '<a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> | Map data: © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
},
|
||||
"DCS": {
|
||||
urlTemplate: 'http://localhost:3000/maps/dcs/{z}/{x}/{y}.png',
|
||||
urlTemplate: 'http://localhost:3000/resources/maps/dcs/{z}/{x}/{y}.png',
|
||||
minZoom: 16,
|
||||
maxZoom: 16,
|
||||
maxZoom: 20,
|
||||
attribution: 'Eagle Dynamics'
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true
|
||||
"args": ["./configs/LasVegas/LasVegas.yml"]
|
||||
}
|
||||
]
|
||||
}
|
||||
81
scripts/python/generateMaps/capture_screen.py
Normal file
81
scripts/python/generateMaps/capture_screen.py
Normal file
@ -0,0 +1,81 @@
|
||||
import math
|
||||
import requests
|
||||
import json
|
||||
import pyautogui
|
||||
import time
|
||||
import os
|
||||
|
||||
from pyproj import Geod
|
||||
from fastkml import kml
|
||||
from shapely import wkt
|
||||
|
||||
def deg_to_num(lat_deg, lon_deg, zoom):
|
||||
lat_rad = math.radians(lat_deg)
|
||||
n = 1 << zoom
|
||||
xtile = int((lon_deg + 180.0) / 360.0 * n)
|
||||
ytile = int((1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n)
|
||||
return xtile, ytile
|
||||
|
||||
def num_to_deg(xtile, ytile, zoom):
|
||||
n = 1 << zoom
|
||||
lon_deg = xtile / n * 360.0 - 180.0
|
||||
lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
|
||||
lat_deg = math.degrees(lat_rad)
|
||||
return lat_deg, lon_deg
|
||||
|
||||
def run(map_config):
|
||||
with open(map_config["boundary_file"], 'rt', encoding="utf-8") as bp:
|
||||
# Read the config file
|
||||
doc = bp.read()
|
||||
k = kml.KML()
|
||||
k.from_string(doc)
|
||||
|
||||
geod = Geod(ellps="WGS84")
|
||||
features = [f for f in list(k.features()) if not f.isopen]
|
||||
print(f"Found {len(features)} closed features in the provided kml file")
|
||||
|
||||
area = 0
|
||||
for feature in features:
|
||||
for sub_feature in list(feature.features()):
|
||||
geo = sub_feature.geometry
|
||||
|
||||
start_lat = geo.bounds[1]
|
||||
start_lng = geo.bounds[0]
|
||||
end_lat = geo.bounds[3]
|
||||
end_lng = geo.bound[2]
|
||||
|
||||
# Find the starting and ending points
|
||||
start_X, start_Y = deg_to_num(start_lat, start_lng, zoom)
|
||||
end_X, end_Y = deg_to_num(end_lat, end_lng, zoom)
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
# Create output folder
|
||||
if not os.path.exists("output"):
|
||||
os.mkdir("output")
|
||||
|
||||
# Start looping
|
||||
n = 1
|
||||
total = math.floor((end_X - start_X) / 2) * math.floor((end_Y - start_Y) / 2)
|
||||
for X in range(start_X, end_X, 2):
|
||||
for Y in range(start_Y, end_Y, 2):
|
||||
# Find the center of the screen
|
||||
center_lat, center_lng = num_to_deg(X + 1, Y + 1, zoom)
|
||||
center_alt = camera_altitude(center_lat)
|
||||
|
||||
# Making PUT request
|
||||
data = json.dumps({'lat': center_lat, 'lng': center_lng, 'alt': center_alt})
|
||||
r = requests.put('http://localhost:8080', data = data)
|
||||
|
||||
# Take and save screenshot
|
||||
screenshot = pyautogui.screenshot()
|
||||
screenshot.save(f"output/{X + 1}_{Y + 1}_{zoom}.png")
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
print(f"Shot {n} of {total}")
|
||||
n = n + 1
|
||||
|
||||
|
||||
|
||||
|
||||
84
scripts/python/generateMaps/configs/LasVegas/LasVegas.kml
Normal file
84
scripts/python/generateMaps/configs/LasVegas/LasVegas.kml
Normal file
@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<Document>
|
||||
<name>Senza titolo</name>
|
||||
<gx:CascadingStyle kml:id="__managed_style_29D7120C702F06CED14C">
|
||||
<Style>
|
||||
<IconStyle>
|
||||
<scale>1.2</scale>
|
||||
<Icon>
|
||||
<href>https://earth.google.com/earth/rpc/cc/icon?color=1976d2&id=2000&scale=4</href>
|
||||
</Icon>
|
||||
<hotSpot x="64" y="128" xunits="pixels" yunits="insetPixels"/>
|
||||
</IconStyle>
|
||||
<LabelStyle>
|
||||
</LabelStyle>
|
||||
<LineStyle>
|
||||
<color>ff2dc0fb</color>
|
||||
<width>6</width>
|
||||
</LineStyle>
|
||||
<PolyStyle>
|
||||
<color>40ffffff</color>
|
||||
</PolyStyle>
|
||||
<BalloonStyle>
|
||||
<displayMode>hide</displayMode>
|
||||
</BalloonStyle>
|
||||
</Style>
|
||||
</gx:CascadingStyle>
|
||||
<gx:CascadingStyle kml:id="__managed_style_1E6AF60F852F06CED14C">
|
||||
<Style>
|
||||
<IconStyle>
|
||||
<Icon>
|
||||
<href>https://earth.google.com/earth/rpc/cc/icon?color=1976d2&id=2000&scale=4</href>
|
||||
</Icon>
|
||||
<hotSpot x="64" y="128" xunits="pixels" yunits="insetPixels"/>
|
||||
</IconStyle>
|
||||
<LabelStyle>
|
||||
</LabelStyle>
|
||||
<LineStyle>
|
||||
<color>ff2dc0fb</color>
|
||||
<width>4</width>
|
||||
</LineStyle>
|
||||
<PolyStyle>
|
||||
<color>40ffffff</color>
|
||||
</PolyStyle>
|
||||
<BalloonStyle>
|
||||
<displayMode>hide</displayMode>
|
||||
</BalloonStyle>
|
||||
</Style>
|
||||
</gx:CascadingStyle>
|
||||
<StyleMap id="__managed_style_05AC9C65832F06CED14C">
|
||||
<Pair>
|
||||
<key>normal</key>
|
||||
<styleUrl>#__managed_style_1E6AF60F852F06CED14C</styleUrl>
|
||||
</Pair>
|
||||
<Pair>
|
||||
<key>highlight</key>
|
||||
<styleUrl>#__managed_style_29D7120C702F06CED14C</styleUrl>
|
||||
</Pair>
|
||||
</StyleMap>
|
||||
<Placemark id="04A23CB2A82F06CED14C">
|
||||
<name>Poligono senza titolo</name>
|
||||
<LookAt>
|
||||
<longitude>-115.7575513617584</longitude>
|
||||
<latitude>36.45909683572987</latitude>
|
||||
<altitude>1668.83938821393</altitude>
|
||||
<heading>0</heading>
|
||||
<tilt>0</tilt>
|
||||
<gx:fovy>35</gx:fovy>
|
||||
<range>382627.9679017514</range>
|
||||
<altitudeMode>absolute</altitudeMode>
|
||||
</LookAt>
|
||||
<styleUrl>#__managed_style_05AC9C65832F06CED14C</styleUrl>
|
||||
<Polygon>
|
||||
<outerBoundaryIs>
|
||||
<LinearRing>
|
||||
<coordinates>
|
||||
-115.5765603362792,35.92113103850846,0 -114.7667743850415,35.914903046417,0 -114.7800419428627,36.39467581209903,0 -115.6198023719551,36.39886214564519,0 -115.5765603362792,35.92113103850846,0
|
||||
</coordinates>
|
||||
</LinearRing>
|
||||
</outerBoundaryIs>
|
||||
</Polygon>
|
||||
</Placemark>
|
||||
</Document>
|
||||
</kml>
|
||||
@ -0,0 +1,4 @@
|
||||
{
|
||||
'output_directory': './LasVegas', # Where to save the output files
|
||||
'boundary_file': './configs/LasVegas/LasVegas.kml'
|
||||
}
|
||||
10
scripts/python/generateMaps/configs/screen_properties.yml
Normal file
10
scripts/python/generateMaps/configs/screen_properties.yml
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
'width': 1920, # The width of your screen, in pixels
|
||||
'height': 1080, # The height of your screen, in pixels
|
||||
'geo_resolution': 1.0 # The resolution of the map on the screen, in meters per pixel.
|
||||
# To measure this value, first set the F10 map at the desired zoom level.
|
||||
# Then, use F10's map measure tool, and measure the width of the screen in meters.
|
||||
# Finally, divide that value by the width in pixels.
|
||||
# A good value would be around 1 meter per pixel, meaning a 1920px wide map would measure about 1 nautical mile across on the F10 map
|
||||
# Lower values will produce higher resolution maps, but beware of space usage!
|
||||
}
|
||||
@ -1,12 +1,20 @@
|
||||
import os
|
||||
from PIL import Image
|
||||
import concurrent.futures
|
||||
import math
|
||||
|
||||
# correction parameters
|
||||
|
||||
# NTTR
|
||||
rotation = math.degrees(0.01895)
|
||||
scale = 0.973384
|
||||
|
||||
zoom = 16
|
||||
path = "output"
|
||||
|
||||
def crop_image(filename):
|
||||
img = Image.open(os.path.join(path, filename))
|
||||
img = Image.open(os.path.join(path, filename)).rotate(-rotation)
|
||||
img = img.resize((math.floor(img.width * scale), math.floor(img.height * scale)))
|
||||
center_X, center_Y = filename.removesuffix(".png").split("_")
|
||||
center_X = int(center_X)
|
||||
center_Y = int(center_Y)
|
||||
|
||||
66
scripts/python/generateMaps/main.py
Normal file
66
scripts/python/generateMaps/main.py
Normal file
@ -0,0 +1,66 @@
|
||||
import sys
|
||||
import yaml
|
||||
from pyproj import Geod
|
||||
from fastkml import kml
|
||||
from shapely import wkt
|
||||
from datetime import timedelta
|
||||
|
||||
import capture_screen
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
print("Please provide a configuration file as first argument. You can also drop the configuration file on this script to run it.")
|
||||
else:
|
||||
config_file = sys.argv[1]
|
||||
print(f"Using config file: {config_file}")
|
||||
|
||||
with open('configs/screen_properties.yml', 'r') as sp:
|
||||
with open(config_file, 'r') as cp:
|
||||
screen_config = yaml.safe_load(sp)
|
||||
map_config = yaml.safe_load(cp)
|
||||
|
||||
print("#################################################################################################################################################")
|
||||
print("# IMPORTANT NOTE: the screen properties must be configured according to your screen and desired zoom level. Make sure you set them accordingly. #")
|
||||
print("#################################################################################################################################################")
|
||||
|
||||
print("Screen parameters:")
|
||||
print(f"-> Screen width: {screen_config["width"]}px")
|
||||
print(f"-> Screen height: {screen_config["height"]}px")
|
||||
print(f"-> Geographic resolution: {screen_config["geo_resolution"]} meters/pixel")
|
||||
|
||||
print("Map parameters:")
|
||||
print(f"-> Output directory: {map_config["output_directory"]}")
|
||||
print(f"-> Boundary file: {map_config["boundary_file"]}")
|
||||
|
||||
with open(map_config["boundary_file"], 'rt', encoding="utf-8") as bp:
|
||||
# Read the config file and compute the total area of the covered map
|
||||
doc = bp.read()
|
||||
k = kml.KML()
|
||||
k.from_string(doc)
|
||||
|
||||
geod = Geod(ellps="WGS84")
|
||||
features = [f for f in list(k.features()) if not f.isopen]
|
||||
print(f"Found {len(features)} closed features in the provided kml file")
|
||||
|
||||
area = 0
|
||||
for feature in features:
|
||||
for sub_feature in list(feature.features()):
|
||||
geo = sub_feature.geometry
|
||||
area += abs(geod.geometry_area_perimeter(wkt.loads(geo.wkt))[0])
|
||||
|
||||
tile_size = 256 * screen_config["geo_resolution"] # meters
|
||||
tiles_per_screenshot = int(screen_config["width"] / 256) * int(screen_config["height"] / 256)
|
||||
tiles_num = int(area / (tile_size * tile_size))
|
||||
screenshots_num = int(tiles_num / tiles_per_screenshot)
|
||||
total_time = int(screenshots_num / 1.0)
|
||||
|
||||
print(f"Total area: {int(area / 1e6)} square kilometers")
|
||||
print(f"Estimated number of tiles: {tiles_num}")
|
||||
print(f"Estimated number of screenshots: {screenshots_num}")
|
||||
print(f"Estimated time to complete: {timedelta(seconds=total_time)} (hh:mm:ss)")
|
||||
print("The script is ready to go. After you press any key, it will wait for 5 seconds, and then it will start.")
|
||||
|
||||
input("Press any key to continue...")
|
||||
capture_screen.run(map_config)
|
||||
|
||||
|
||||
|
||||
23
scripts/python/generateMaps/requirements.txt
Normal file
23
scripts/python/generateMaps/requirements.txt
Normal file
@ -0,0 +1,23 @@
|
||||
certifi==2024.2.2
|
||||
charset-normalizer==3.3.2
|
||||
fastkml==0.12
|
||||
idna==3.6
|
||||
MouseInfo==0.1.3
|
||||
numpy==1.26.4
|
||||
pillow==10.2.0
|
||||
PyAutoGUI==0.9.54
|
||||
pygeoif==0.7
|
||||
PyGetWindow==0.0.9
|
||||
PyMsgBox==1.0.9
|
||||
pyperclip==1.8.2
|
||||
pyproj==3.6.1
|
||||
PyRect==0.2.0
|
||||
PyScreeze==0.1.30
|
||||
python-dateutil==2.8.2
|
||||
pytweening==1.2.0
|
||||
PyYAML==6.0.1
|
||||
requests==2.31.0
|
||||
setuptools==69.1.0
|
||||
shapely==2.0.3
|
||||
six==1.16.0
|
||||
urllib3==2.2.1
|
||||
@ -1,75 +0,0 @@
|
||||
import math
|
||||
import requests
|
||||
import json
|
||||
import pyautogui
|
||||
import time
|
||||
import os
|
||||
|
||||
# parameters
|
||||
start_lat = 36.31669444 # degs
|
||||
start_lng = -115.38336111 # degs
|
||||
|
||||
end_lat = 35.93336111 # degs
|
||||
end_lng = -114.95002778 # degs
|
||||
|
||||
fov = 10 # deg
|
||||
zoom = 16
|
||||
|
||||
# constants
|
||||
C = 40075016.686 # meters
|
||||
|
||||
def deg_to_num(lat_deg, lon_deg, zoom):
|
||||
lat_rad = math.radians(lat_deg)
|
||||
n = 1 << zoom
|
||||
xtile = int((lon_deg + 180.0) / 360.0 * n)
|
||||
ytile = int((1.0 - math.asinh(math.tan(lat_rad)) / math.pi) / 2.0 * n)
|
||||
return xtile, ytile
|
||||
|
||||
def num_to_deg(xtile, ytile, zoom):
|
||||
n = 1 << zoom
|
||||
lon_deg = xtile / n * 360.0 - 180.0
|
||||
lat_rad = math.atan(math.sinh(math.pi * (1 - 2 * ytile / n)))
|
||||
lat_deg = math.degrees(lat_rad)
|
||||
return lat_deg, lon_deg
|
||||
|
||||
def camera_altitude(lat_deg):
|
||||
mpp = C * math.cos(math.radians(lat_deg)) / math.pow(2, zoom + 8)
|
||||
d = mpp * 1920
|
||||
alt = d / 2 * 1 / math.tan(math.radians(fov) / 2)
|
||||
return alt
|
||||
|
||||
# Find the starting and ending points
|
||||
start_X, start_Y = deg_to_num(start_lat, start_lng, zoom)
|
||||
end_X, end_Y = deg_to_num(end_lat, end_lng, zoom)
|
||||
|
||||
time.sleep(2)
|
||||
|
||||
# Create output folder
|
||||
if not os.path.exists("output"):
|
||||
os.mkdir("output")
|
||||
|
||||
# Start looping
|
||||
n = 1
|
||||
total = math.floor((end_X - start_X) / 2) * math.floor((end_Y - start_Y) / 2)
|
||||
for X in range(start_X, end_X, 2):
|
||||
for Y in range(start_Y, end_Y, 2):
|
||||
# Find the center of the screen
|
||||
center_lat, center_lng = num_to_deg(X + 1, Y + 1, zoom)
|
||||
center_alt = camera_altitude(center_lat)
|
||||
|
||||
# Making PUT request
|
||||
data = json.dumps({'lat': center_lat, 'lng': center_lng, 'alt': center_alt})
|
||||
r = requests.put('http://localhost:8080', data = data)
|
||||
|
||||
# Take and save screenshot
|
||||
screenshot = pyautogui.screenshot()
|
||||
screenshot.save(f"output/{X + 1}_{Y + 1}_{zoom}.png")
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
print(f"Shot {n} of {total}")
|
||||
n = n + 1
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user