mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Completed camera control server in lua and more work on merging algorithm
This commit is contained in:
parent
05e0cc393a
commit
832568aa00
@ -582,7 +582,8 @@ export class Map extends L.Map {
|
||||
|
||||
setCameraControlMode(newCameraControlMode: string) {
|
||||
this.#cameraControlMode = newCameraControlMode;
|
||||
this.#broadcastPosition();
|
||||
if (this.#slaveDCSCamera)
|
||||
this.#broadcastPosition();
|
||||
}
|
||||
|
||||
/* Event handlers */
|
||||
@ -773,7 +774,7 @@ export class Map extends L.Map {
|
||||
try {
|
||||
groundElevation = parseFloat(response);
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("PUT", `http://localhost:${this.#cameraControlPort}`);
|
||||
xmlHttp.open("PUT", `http://127.0.0.1:${this.#cameraControlPort}`);
|
||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
||||
|
||||
const C = 40075016.686;
|
||||
@ -981,7 +982,7 @@ export class Map extends L.Map {
|
||||
|
||||
#checkCameraPort(){
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("OPTIONS", `http://localhost:${this.#cameraControlPort}`);
|
||||
xmlHttp.open("OPTIONS", `http://127.0.0.1:${this.#cameraControlPort}`);
|
||||
xmlHttp.onload = (res: any) => {
|
||||
if (xmlHttp.status == 200)
|
||||
this.#setSlaveDCSCameraAvailable(true);
|
||||
|
||||
214
scripts/lua/camera/OlympusCameraControl.lua
Normal file
214
scripts/lua/camera/OlympusCameraControl.lua
Normal file
@ -0,0 +1,214 @@
|
||||
local _prevLuaExportStart = LuaExportStart
|
||||
local _prevLuaExportBeforeNextFrame = LuaExportBeforeNextFrame
|
||||
local _prevLuaExportStop = LuaExportStop
|
||||
|
||||
local server = nil
|
||||
local port = 3003
|
||||
local headers = "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Methods: PUT, OPTIONS\r\nAccess-Control-Allow-Headers: *\r\nAccess-Control-Max-Age: 86400\r\nVary: Accept-Encoding, Origin\r\nKeep-Alive: timeout=2, max=100\r\nConnection: Keep-Alive\r\n\r\n"
|
||||
|
||||
function startTCPServer()
|
||||
log.write('OLYMPUSCAMERACONTROL.EXPORT.LUA', log.INFO, 'Starting TCP Server')
|
||||
package.path = package.path..";"..lfs.currentdir().."/LuaSocket/?.lua"
|
||||
package.cpath = package.cpath..";"..lfs.currentdir().."/LuaSocket/?.dll"
|
||||
|
||||
socket = require("socket")
|
||||
|
||||
server = assert(socket.bind("127.0.0.1", port))
|
||||
if server then
|
||||
server:setoption("tcp-nodelay", true)
|
||||
server:settimeout(0)
|
||||
log.write('OLYMPUSCAMERACONTROL.EXPORT.LUA', log.INFO, 'TCP Server listening on port ' .. port)
|
||||
else
|
||||
log.write('OLYMPUSCAMERACONTROL.EXPORT.LUA', log.INFO, 'TCP Server did not start successfully')
|
||||
end
|
||||
end
|
||||
|
||||
function receiveTCP()
|
||||
if server then
|
||||
-- Accept a new connection without blocking
|
||||
local client = server:accept()
|
||||
|
||||
if client then
|
||||
-- Set the timeout of the connection to 5ms
|
||||
client:settimeout(0)
|
||||
client:setoption("tcp-nodelay", true)
|
||||
|
||||
local acc = ""
|
||||
local data = ""
|
||||
|
||||
-- Start receiving data, accumulate it in acc
|
||||
while data ~= nil do
|
||||
-- Receive a new line
|
||||
data, err, partial = client:receive('*l')
|
||||
if data then
|
||||
-- If we receive an empty string it means the header section of the message is over
|
||||
if data == "" then
|
||||
-- Is this an OPTIONS request?
|
||||
if string.find(acc, "OPTIONS") ~= nil then
|
||||
client:send("HTTP/1.1 200 OK\r\n" .. headers)
|
||||
client:close()
|
||||
|
||||
-- Is this a PUT request?
|
||||
elseif string.find(acc, "PUT") ~= nil then
|
||||
-- Extract the length of the body
|
||||
local contentLength = string.match(acc, "Content%-Length: (%d+)")
|
||||
if contentLength ~= nil then
|
||||
-- Receive the body
|
||||
body, err, partial = client:receive(tonumber(contentLength))
|
||||
if body ~= nil then
|
||||
local lat = string.match(body, '"lat":%s*([%+%-]?[%d%.]+)%s*[},]')
|
||||
local lng = string.match(body, '"lng":%s*([%+%-]?[%d%.]+)%s*[},]')
|
||||
local alt = string.match(body, '"alt":%s*([%+%-]?[%d%.]+)%s*[},]')
|
||||
local mode = string.match(body, '"mode":%s*"(%a+)"%s*[},]')
|
||||
|
||||
if lat ~= nil and lng ~= nil then
|
||||
client:send("HTTP/1.1 200 OK\r\n" .. headers)
|
||||
|
||||
local position = {}
|
||||
position["lat"] = tonumber(lat)
|
||||
position["lng"] = tonumber(lng)
|
||||
if alt ~= nil then
|
||||
position["alt"] = tonumber(alt)
|
||||
end
|
||||
|
||||
-- F11 view
|
||||
if mode == "live" or mode == nil then
|
||||
LoSetCommand(158)
|
||||
-- F10 view
|
||||
elseif mode == "map" then
|
||||
LoSetCommand(15)
|
||||
end
|
||||
|
||||
client:send(setCameraPosition(position))
|
||||
client:close()
|
||||
else
|
||||
client:send("HTTP/1.1 500 ERROR\r\n" .. headers)
|
||||
client:close()
|
||||
end
|
||||
else
|
||||
log.write('OLYMPUSCAMERACONTROL.EXPORT.LUA', log.ERROR, err)
|
||||
end
|
||||
end
|
||||
client:close()
|
||||
break
|
||||
end
|
||||
else
|
||||
-- Keep accumulating the incoming data
|
||||
acc = acc .. " " .. data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function stopTCPServer()
|
||||
if server then
|
||||
log.write('OLYMPUSCAMERACONTROL.EXPORT.LUA', log.INFO, 'Stopping TCP Server')
|
||||
server:close()
|
||||
end
|
||||
server = nil
|
||||
end
|
||||
|
||||
function setCameraPosition(position)
|
||||
-- Get the old camera position
|
||||
local oldPos = LoGetCameraPosition()
|
||||
|
||||
-- Extract the commanded position
|
||||
local point = LoGeoCoordinatesToLoCoordinates(position.lng, position.lat)
|
||||
local pointNorth = LoGeoCoordinatesToLoCoordinates(position.lng, position.lat + 0.1)
|
||||
|
||||
-- Compute the local map rotation and scale and send it back to the server
|
||||
local rotation = math.atan2(pointNorth.z - point.z, pointNorth.x - point.x)
|
||||
|
||||
-- If no altitude is provided, preserve the current camera altitude
|
||||
local altitude = nil
|
||||
if position.alt == nil then
|
||||
altitude = oldPos.p.y
|
||||
else
|
||||
altitude = position.alt
|
||||
end
|
||||
|
||||
-- Set the camera position
|
||||
local pos =
|
||||
{
|
||||
x = {x = 0, y = -1, z = 0},
|
||||
y = {x = 1, y = 0, z = 0},
|
||||
z = {x = 0, y = 0, z = 1},
|
||||
p = {x = point.x, y = altitude, z = point.z}
|
||||
}
|
||||
LoSetCameraPosition(pos)
|
||||
|
||||
return '{"northRotation": ' .. rotation .. '}'
|
||||
end
|
||||
|
||||
LuaExportStart = function()
|
||||
package.path = package.path..";"..lfs.currentdir().."/LuaSocket/?.lua"
|
||||
package.cpath = package.cpath..";"..lfs.currentdir().."/LuaSocket/?.dll"
|
||||
|
||||
startTCPServer()
|
||||
|
||||
-- call original
|
||||
if _prevLuaExportStart then
|
||||
_status, _result = pcall(_prevLuaExportStart)
|
||||
if not _status then
|
||||
log.write('OLYMPUSCAMERACONTROL.EXPORT.LUA', log.ERROR, 'ERROR Calling other LuaExportStart from another script', _result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
LuaExportBeforeNextFrame = function()
|
||||
receiveTCP()
|
||||
|
||||
-- call original
|
||||
if _prevLuaExportBeforeNextFrame then
|
||||
_status, _result = pcall(_prevLuaExportBeforeNextFrame)
|
||||
if not _status then
|
||||
log.write('OLYMPUSCAMERACONTROL.EXPORT.LUA', log.ERROR, 'ERROR Calling other LuaExportBeforeNextFrame from another script', _result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
LuaExportStop = function()
|
||||
stopTCPServer()
|
||||
|
||||
-- call original
|
||||
if _prevLuaExportStop then
|
||||
_status, _result = pcall(_prevLuaExportStop)
|
||||
if not _status then
|
||||
log.write('OLYMPUSCAMERACONTROL.EXPORT.LUA', log.ERROR, 'ERROR Calling other LuaExportStop from another script', _result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function serializeTable(val, name, skipnewlines, depth)
|
||||
skipnewlines = skipnewlines or false
|
||||
depth = depth or 0
|
||||
|
||||
local tmp = string.rep(" ", depth)
|
||||
if name then
|
||||
if type(name) == "number" then
|
||||
tmp = tmp .. "[" .. name .. "]" .. " = "
|
||||
else
|
||||
tmp = tmp .. name .. " = "
|
||||
end
|
||||
end
|
||||
|
||||
if type(val) == "table" then
|
||||
tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")
|
||||
for k, v in pairs(val) do
|
||||
tmp = tmp .. serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
|
||||
end
|
||||
tmp = tmp .. string.rep(" ", depth) .. "}"
|
||||
elseif type(val) == "number" then
|
||||
tmp = tmp .. tostring(val)
|
||||
elseif type(val) == "string" then
|
||||
tmp = tmp .. string.format("%q", val)
|
||||
elseif type(val) == "boolean" then
|
||||
tmp = tmp .. (val and "true" or "false")
|
||||
else
|
||||
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
|
||||
end
|
||||
|
||||
return tmp
|
||||
end
|
||||
@ -10,7 +10,7 @@
|
||||
"request": "launch",
|
||||
"program": "main.py",
|
||||
"console": "integratedTerminal",
|
||||
"args": ["./configs/NTTR/config.yml"]
|
||||
"args": ["./configs/Caucasus/HighResolution.yml"]
|
||||
}
|
||||
]
|
||||
}
|
||||
37
scripts/python/map_generator/airbases_to_kml.py
Normal file
37
scripts/python/map_generator/airbases_to_kml.py
Normal file
@ -0,0 +1,37 @@
|
||||
import sys
|
||||
from fastkml import kml
|
||||
from pygeoif.geometry import Polygon
|
||||
import json
|
||||
import math
|
||||
|
||||
# constants
|
||||
C = 40075016.686 # meters, Earth equatorial circumference
|
||||
R = C / (2 * math.pi) # meters, Earth equatorial radius
|
||||
W = 10000 # meters, size of the square around the airbase
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
print("Please provide a json file as first argument. You can also drop the json file on this script to run it.")
|
||||
else:
|
||||
input_file = sys.argv[1]
|
||||
k = kml.KML()
|
||||
ns = '{http://www.opengis.net/kml/2.2}'
|
||||
|
||||
d = kml.Document(ns, 'docid', 'doc name', 'doc description')
|
||||
k.append(d)
|
||||
|
||||
with open(input_file) as jp:
|
||||
j = json.load(jp)
|
||||
|
||||
for point in j['airbases'].values():
|
||||
p = kml.Placemark(ns, 'id', 'name', 'description')
|
||||
lat = point['latitude']
|
||||
lng = point['longitude']
|
||||
|
||||
latDelta = math.degrees(W / R)
|
||||
lngDelta = math.degrees(W / (R * math.cos(math.radians(lat))))
|
||||
|
||||
p.geometry = Polygon([(lng - lngDelta, lat - latDelta), (lng - lngDelta, lat + latDelta), (lng + lngDelta, lat + latDelta), (lng + lngDelta, lat - latDelta)])
|
||||
d.append(p)
|
||||
|
||||
with open(input_file.removesuffix('.json')+'.kml', 'w') as kp:
|
||||
kp.writelines(k.to_string(prettyprint=True))
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
'output_directory': '.\Caucasus', # Where to save the output files
|
||||
'boundary_file': '.\configs\Caucasus\airbases.kml', # Input kml file setting the boundary of the map to create
|
||||
'zoom_factor': 0.1 # [0: maximum zoom in (things look very big), 1: maximum zoom out (things look very small)]
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
<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_2F9039BE1B2F0ACE3361">
|
||||
<gx:CascadingStyle kml:id="__managed_style_280E5494AE2F24E92C22">
|
||||
<Style>
|
||||
<IconStyle>
|
||||
<scale>1.2</scale>
|
||||
@ -25,7 +25,7 @@
|
||||
</BalloonStyle>
|
||||
</Style>
|
||||
</gx:CascadingStyle>
|
||||
<gx:CascadingStyle kml:id="__managed_style_1FB08D70372F0ACE3361">
|
||||
<gx:CascadingStyle kml:id="__managed_style_1EB9027B622F24E92C22">
|
||||
<Style>
|
||||
<IconStyle>
|
||||
<Icon>
|
||||
@ -47,57 +47,34 @@
|
||||
</BalloonStyle>
|
||||
</Style>
|
||||
</gx:CascadingStyle>
|
||||
<StyleMap id="__managed_style_0152B588AD2F0ACE3361">
|
||||
<StyleMap id="__managed_style_0F57E9B9782F24E92C22">
|
||||
<Pair>
|
||||
<key>normal</key>
|
||||
<styleUrl>#__managed_style_1FB08D70372F0ACE3361</styleUrl>
|
||||
<styleUrl>#__managed_style_1EB9027B622F24E92C22</styleUrl>
|
||||
</Pair>
|
||||
<Pair>
|
||||
<key>highlight</key>
|
||||
<styleUrl>#__managed_style_2F9039BE1B2F0ACE3361</styleUrl>
|
||||
<styleUrl>#__managed_style_280E5494AE2F24E92C22</styleUrl>
|
||||
</Pair>
|
||||
</StyleMap>
|
||||
<Placemark id="0389FD622C2F0ACE3361">
|
||||
<Placemark id="0975D432582F24E92C1E">
|
||||
<name>Poligono senza titolo</name>
|
||||
<LookAt>
|
||||
<longitude>-115.0437621195802</longitude>
|
||||
<latitude>36.2404454323581</latitude>
|
||||
<altitude>568.1069300877758</altitude>
|
||||
<longitude>37.25019544589698</longitude>
|
||||
<latitude>44.41771380726969</latitude>
|
||||
<altitude>-138.6844933247498</altitude>
|
||||
<heading>0</heading>
|
||||
<tilt>0</tilt>
|
||||
<gx:fovy>35</gx:fovy>
|
||||
<range>21582.08160380367</range>
|
||||
<range>3831683.119853139</range>
|
||||
<altitudeMode>absolute</altitudeMode>
|
||||
</LookAt>
|
||||
<styleUrl>#__managed_style_0152B588AD2F0ACE3361</styleUrl>
|
||||
<styleUrl>#__managed_style_0F57E9B9782F24E92C22</styleUrl>
|
||||
<Polygon>
|
||||
<outerBoundaryIs>
|
||||
<LinearRing>
|
||||
<coordinates>
|
||||
-115.0657741984423,36.20908708202413,0 -115.0064821223275,36.20969233542438,0 -115.0077003574054,36.25251471885595,0 -115.0604905801644,36.25236266770626,0 -115.0657741984423,36.20908708202413,0
|
||||
</coordinates>
|
||||
</LinearRing>
|
||||
</outerBoundaryIs>
|
||||
</Polygon>
|
||||
</Placemark>
|
||||
<Placemark id="077CD022A52F0ACE74D0">
|
||||
<name>Poligono senza titolo</name>
|
||||
<LookAt>
|
||||
<longitude>-115.144039076036</longitude>
|
||||
<latitude>36.07599823986274</latitude>
|
||||
<altitude>636.6854074835677</altitude>
|
||||
<heading>0</heading>
|
||||
<tilt>0</tilt>
|
||||
<gx:fovy>35</gx:fovy>
|
||||
<range>14114.14487087633</range>
|
||||
<altitudeMode>absolute</altitudeMode>
|
||||
</LookAt>
|
||||
<styleUrl>#__managed_style_0152B588AD2F0ACE3361</styleUrl>
|
||||
<Polygon>
|
||||
<outerBoundaryIs>
|
||||
<LinearRing>
|
||||
<coordinates>
|
||||
-115.1866576903507,36.06787728722109,0 -115.1107872218999,36.06765965614163,0 -115.1145241760975,36.10294242539007,0 -115.1779590799479,36.10179027153036,0 -115.1866576903507,36.06787728722109,0
|
||||
32.46459319237173,45.67416695848307,0 32.2740650283415,45.2221541106433,0 33.22174616520244,44.4837859435444,0 34.05427109764131,44.2149221586376,0 34.96485577272431,44.60230684639296,0 35.50552864748745,44.8069362633187,0 36.446105774871,44.84425518198143,0 36.76914203317659,44.70347050722764,0 38.22313992004164,44.3163345847565,0 39.43106567523965,43.72064977016311,0 40.23832274382622,43.06831352526857,0 41.01327578994438,42.67925159935859,0 41.34464189582403,42.34329512558789,0 41.16749495371268,41.74956946999534,0 40.80780496107725,41.39360013128164,0 39.98364177441992,41.27272565351572,0 39.42209428526464,41.27830763089842,0 38.82136897872954,41.2291415593637,0 38.78900701766597,39.59331113999448,0 46.4826445997655,39.11657164682355,0 46.83937081793388,45.04996086829865,0 46.88987497227086,47.59122144470205,0 32.29992865035658,47.73230965442627,0 32.46459319237173,45.67416695848307,0
|
||||
</coordinates>
|
||||
</LinearRing>
|
||||
</outerBoundaryIs>
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
'output_directory': '.\Caucasus', # Where to save the output files
|
||||
'boundary_file': '.\configs\Caucasus\LowResolution.kml', # Input kml file setting the boundary of the map to create
|
||||
'zoom_factor': 0.5 # [0: maximum zoom in (things look very big), 1: maximum zoom out (things look very small)]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,5 @@
|
||||
{
|
||||
'output_directory': '.\Caucasus', # Where to save the output files
|
||||
'boundary_file': '.\configs\Caucasus\MediumResolution.kml', # Input kml file setting the boundary of the map to create
|
||||
'zoom_factor': 0.25 # [0: maximum zoom in (things look very big), 1: maximum zoom out (things look very small)]
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
{"airbases":{"1":{"callsign":"Anapa-Vityazevo","coalition":"neutral","latitude":45.013174733771677,"longitude":37.359783477555922},"10":{"callsign":"Gudauta","coalition":"neutral","latitude":43.124233340197144,"longitude":40.564175768400638},"11":{"callsign":"Batumi","coalition":"neutral","latitude":41.603279859649049,"longitude":41.609275483509791},"12":{"callsign":"Senaki-Kolkhi","coalition":"neutral","latitude":42.238728081573278,"longitude":42.061021312855914},"13":{"callsign":"Kobuleti","coalition":"neutral","latitude":41.93210535345338,"longitude":41.876483823101026},"14":{"callsign":"Kutaisi","coalition":"neutral","latitude":42.179153937689627,"longitude":42.495684077400142},"15":{"callsign":"Mineralnye Vody","coalition":"neutral","latitude":44.218646823806807,"longitude":43.100679733081456},"16":{"callsign":"Nalchik","coalition":"neutral","latitude":43.510071438529849,"longitude":43.625108736097914},"17":{"callsign":"Mozdok","coalition":"neutral","latitude":43.791303250938249,"longitude":44.620327262102009},"18":{"callsign":"Tbilisi-Lochini","coalition":"neutral","latitude":41.674720064437075,"longitude":44.946875226153338},"19":{"callsign":"Soganlug","coalition":"neutral","latitude":41.641163266786613,"longitude":44.947183065316693},"2":{"callsign":"Krasnodar-Center","coalition":"neutral","latitude":45.087429883845076,"longitude":38.925202300775062},"20":{"callsign":"Vaziani","coalition":"neutral","latitude":41.637735936261556,"longitude":45.019090938460067},"21":{"callsign":"Beslan","coalition":"neutral","latitude":43.208500987380937,"longitude":44.588922553542936},"3":{"callsign":"Novorossiysk","coalition":"neutral","latitude":44.673329604126899,"longitude":37.786226060479564},"4":{"callsign":"Krymsk","coalition":"neutral","latitude":44.961383022734175,"longitude":37.985886938697085},"5":{"callsign":"Maykop-Khanskaya","coalition":"neutral","latitude":44.67144025735508,"longitude":40.021427482235985},"6":{"callsign":"Gelendzhik","coalition":"neutral","latitude":44.56767458600406,"longitude":38.004146350528103},"7":{"callsign":"Sochi-Adler","coalition":"neutral","latitude":43.439378434050852,"longitude":39.924231880466095},"8":{"callsign":"Krasnodar-Pashkovsky","coalition":"neutral","latitude":45.0460996415433,"longitude":39.203066906324537},"9":{"callsign":"Sukhumi-Babushara","coalition":"neutral","latitude":42.852741071634995,"longitude":41.142447588488196}},"frameRate":60,"load":0,"sessionHash":"K2n7kpGE9yOaYE4G","time":"1709136685634"}
|
||||
File diff suppressed because one or more lines are too long
@ -1,6 +0,0 @@
|
||||
{
|
||||
'output_directory': '.\LasVegas', # Where to save the output files
|
||||
'boundary_file': '.\configs\LasVegas\boundary.kml', # Input kml file setting the boundary of the map to create
|
||||
'zoom_factor': 0.02, # [0: maximum zoom in (things look very big), 1: maximum zoom out (things look very small)]
|
||||
'geo_width': 1.14
|
||||
}
|
||||
@ -2,7 +2,6 @@ import sys
|
||||
import yaml
|
||||
import json
|
||||
import requests
|
||||
import time
|
||||
|
||||
from pyproj import Geod
|
||||
from fastkml import kml
|
||||
@ -11,6 +10,9 @@ from datetime import timedelta
|
||||
|
||||
import map_generator
|
||||
|
||||
# Port on which the camera control module is listening
|
||||
port = 3003
|
||||
|
||||
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:
|
||||
@ -53,9 +55,12 @@ else:
|
||||
|
||||
if 'geo_width' not in map_config:
|
||||
# Let the user input the size of the screen to compute resolution
|
||||
data = json.dumps({'lat': features[0].geometry.bounds[1], 'lng': features[0].geometry.bounds[0], 'alt': 1350 + map_config['zoom_factor'] * (25000 - 1350)})
|
||||
r = requests.put('http://localhost:8080', data = data)
|
||||
print("The F10 map in your DCS installation was setup. Please, use the measure tool and measure the width of the screen in Nautical Miles")
|
||||
data = json.dumps({'lat': features[0].geometry.bounds[1], 'lng': features[0].geometry.bounds[0], 'alt': 1350 + map_config['zoom_factor'] * (25000 - 1350), 'mode': 'map'})
|
||||
try:
|
||||
r = requests.put(f'http://127.0.0.1:{port}', data = data)
|
||||
print("The F10 map in your DCS installation was setup. Please, use the measure tool and measure the width of the screen in Nautical Miles")
|
||||
except:
|
||||
print("No running DCS instance detected. You can still run the algorithm if you already took the screenshots, otherwise you will not be able to produce a map.")
|
||||
map_config['geo_width'] = input("Insert the width of the screen in Nautical Miles: ")
|
||||
|
||||
map_config['mpps'] = float(map_config['geo_width']) * 1852 / screen_config['width']
|
||||
@ -72,7 +77,7 @@ else:
|
||||
print(f"Estimated time to complete: {timedelta(seconds=total_time * 0.15)} (hh:mm:ss)")
|
||||
input("Press enter to continue...")
|
||||
|
||||
map_generator.run(map_config)
|
||||
map_generator.run(map_config, port)
|
||||
|
||||
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import time
|
||||
import os
|
||||
import yaml
|
||||
import json
|
||||
import numpy
|
||||
|
||||
from fastkml import kml
|
||||
from shapely import wkt, Point
|
||||
@ -19,7 +20,7 @@ tot_futs = 0
|
||||
|
||||
# constants
|
||||
C = 40075016.686 # meters, Earth equatorial circumference
|
||||
R = C / (2 * math.pi)
|
||||
R = C / (2 * math.pi) # meters, Earth equatorial radius
|
||||
|
||||
def deg_to_num(lat_deg, lon_deg, zoom):
|
||||
lat_rad = math.radians(lat_deg)
|
||||
@ -60,9 +61,9 @@ def extract_tiles(n, screenshots_XY, params):
|
||||
n_height = params['n_height']
|
||||
|
||||
XY = screenshots_XY[n]
|
||||
if (os.path.exists(os.path.join(output_directory, "screenshots", f"{f}_{n}.jpg"))):
|
||||
if (os.path.exists(os.path.join(output_directory, "screenshots", f"{f}_{n}_{zoom}.jpg"))):
|
||||
# Open the source screenshot
|
||||
img = Image.open(os.path.join(output_directory, "screenshots", f"{f}_{n}.jpg"))
|
||||
img = Image.open(os.path.join(output_directory, "screenshots", f"{f}_{n}_{zoom}.jpg"))
|
||||
|
||||
# Compute the Web Mercator Projection position of the top left corner of the most centered tile
|
||||
X_center, Y_center = XY[0], XY[1]
|
||||
@ -84,6 +85,7 @@ def extract_tiles(n, screenshots_XY, params):
|
||||
try:
|
||||
os.mkdir(os.path.join(output_directory, "tiles", str(zoom), str(X)))
|
||||
except FileExistsError:
|
||||
# Ignore this error, it means one other thread has already created the folder
|
||||
continue
|
||||
except Exception as e:
|
||||
raise e
|
||||
@ -91,33 +93,53 @@ def extract_tiles(n, screenshots_XY, params):
|
||||
n += 1
|
||||
|
||||
else:
|
||||
raise Exception(f"{os.path.join(output_directory, 'screenshots', f'{f}_{n}.jpg')} missing")
|
||||
raise Exception(f"{os.path.join(output_directory, 'screenshots', f'{f}_{n}_{zoom}.jpg')} missing")
|
||||
|
||||
def merge_tiles(base_path, zoom, tile):
|
||||
X = tile[0]
|
||||
Y = tile[1]
|
||||
|
||||
positions = [(0, 0), (0, 1), (1, 0), (1, 1)]
|
||||
# If the image already exists, open it so we can paste the higher quality data in it
|
||||
if os.path.exists(os.path.join(base_path, str(zoom - 1), str(X), f"{Y}.jpg")):
|
||||
dst = Image.open(os.path.join(base_path, str(zoom - 1), str(X), f"{Y}.jpg"))
|
||||
dst = make_background_transparent(dst)
|
||||
else:
|
||||
dst = Image.new('RGB', (256, 256), (221, 221, 221))
|
||||
|
||||
dst = Image.new('RGB', (256, 256), (0, 0, 0, 0))
|
||||
# Loop on all the 4 subtiles in the tile
|
||||
positions = [(0, 0), (0, 1), (1, 0), (1, 1)]
|
||||
for i in range(0, 4):
|
||||
# Open the subtile, if it exists, and resize it down to 128x128
|
||||
if os.path.exists(os.path.join(base_path, str(zoom), str(2*X + positions[i][0]), f"{2*Y + positions[i][1]}.jpg")):
|
||||
im = Image.open(os.path.join(base_path, str(zoom), str(2*X + positions[i][0]), f"{2*Y + positions[i][1]}.jpg")).resize((128, 128))
|
||||
else:
|
||||
im = Image.new('RGB', (128, 128), (0, 0, 0, 0))
|
||||
dst.paste(im, (positions[i][0] * 128, positions[i][1] * 128))
|
||||
im = make_background_transparent(im)
|
||||
dst.paste(im, (positions[i][0] * 128, positions[i][1] * 128))
|
||||
|
||||
# Create the output folder if it exists
|
||||
if not os.path.exists(os.path.join(base_path, str(zoom - 1), str(X))):
|
||||
try:
|
||||
os.mkdir(os.path.join(base_path, str(zoom - 1), str(X)))
|
||||
except FileExistsError:
|
||||
# Ignore this error, it means one other thread has already created the folder
|
||||
pass
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
dst.save(os.path.join(base_path, str(zoom - 1), str(X), f"{Y}.jpg"), quality=95)
|
||||
|
||||
def run(map_config):
|
||||
# Save the image
|
||||
dst.convert('RGB').save(os.path.join(base_path, str(zoom - 1), str(X), f"{Y}.jpg"), quality=95)
|
||||
|
||||
def make_background_transparent(im):
|
||||
im.putalpha(255)
|
||||
data = numpy.array(im)
|
||||
red, green, blue, alpha = data.T
|
||||
|
||||
# If present, remove any "background" areas
|
||||
background_areas = (red == 221) & (blue == 221) & (green == 221)
|
||||
data[..., :][background_areas.T] = (0, 0, 0, 0) # make transparent
|
||||
|
||||
return Image.fromarray(data)
|
||||
|
||||
def run(map_config, port):
|
||||
global tot_futs, fut_counter
|
||||
|
||||
with open('configs/screen_properties.yml', 'r') as sp:
|
||||
@ -203,8 +225,8 @@ def run(map_config):
|
||||
# Making PUT request
|
||||
# If the number of rows or columns is odd, we need to take the picture at the CENTER of the tile!
|
||||
lat, lng = num_to_deg(XY[0] + (n_width % 2) / 2, XY[1] + (n_height % 2) / 2, zoom)
|
||||
data = json.dumps({'lat': lat, 'lng': lng, 'alt': 1350 + map_config['zoom_factor'] * (25000 - 1350)})
|
||||
r = requests.put('http://localhost:8080', data = data)
|
||||
data = json.dumps({'lat': lat, 'lng': lng, 'alt': 1350 + map_config['zoom_factor'] * (25000 - 1350), 'mode': 'map'})
|
||||
r = requests.put(f'http://127.0.0.1:{port}', data = data)
|
||||
|
||||
geo_data = json.loads(r.text)
|
||||
|
||||
@ -233,7 +255,7 @@ def run(map_config):
|
||||
sy = s_height / m_height
|
||||
|
||||
# Resize, rotate and save the screenshot
|
||||
screenshot.resize((int(sx * screenshot.width), int(sy * screenshot.height))).rotate(math.degrees(geo_data['northRotation'])).save(os.path.join(output_directory, "screenshots", f"{f}_{n}.jpg"), quality=95)
|
||||
screenshot.resize((int(sx * screenshot.width), int(sy * screenshot.height))).rotate(math.degrees(geo_data['northRotation'])).save(os.path.join(output_directory, "screenshots", f"{f}_{n}_{zoom}.jpg"), quality=95)
|
||||
|
||||
printProgressBar(n + 1, len(screenshots_XY))
|
||||
n += 1
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user