mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Merge branch 'release-candidate' of https://github.com/Pax1601/DCSOlympus into release-candidate
This commit is contained in:
commit
539b183da8
1
.gitignore
vendored
1
.gitignore
vendored
@ -39,3 +39,4 @@ frontend/website/plugins/controltips/index.js
|
||||
/frontend/server/public/maps
|
||||
*.pyc
|
||||
/scripts/**/*.jpg
|
||||
manager/manager.log
|
||||
|
||||
@ -31,9 +31,11 @@ module.exports = function (configLocation) {
|
||||
}
|
||||
var app = express();
|
||||
|
||||
var backendAddress = config["backend"]["address"];
|
||||
|
||||
/* Define middleware */
|
||||
app.use(logger('dev'));
|
||||
app.use('/olympus', createProxyMiddleware({ target: `http://${config["backend"]["address"]}:${config["backend"]["port"]}`, changeOrigin: true }));
|
||||
app.use('/olympus', createProxyMiddleware({ target: `http://${backendAddress === '*'? 'localhost': backendAddress}:${config["backend"]["port"]}`, changeOrigin: true }));
|
||||
app.use(bodyParser.json({ limit: '50mb' }));
|
||||
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
@ -458,7 +458,7 @@ module.exports = function (configLocation) {
|
||||
};
|
||||
|
||||
mission(req, res){
|
||||
var ret = {mission: {theatre: "Nevada"}};
|
||||
var ret = {mission: {theatre: "MarianaIslands"}};
|
||||
ret.time = Date.now();
|
||||
|
||||
ret.mission.dateAndTime = {
|
||||
|
||||
@ -122,18 +122,18 @@ export const minimapBoundaries = {
|
||||
],
|
||||
"Falklands": [ // South Atlantic
|
||||
new LatLng(-49.097217, -79.418267),
|
||||
new LatLng(-56.874517,-79.418267),
|
||||
new LatLng(-56.874517, -79.418267),
|
||||
new LatLng(-56.874517, -43.316433),
|
||||
new LatLng(-49.097217, -43.316433),
|
||||
new LatLng(-49.097217, -79.418267)
|
||||
],
|
||||
],
|
||||
"Normandy": [ // Normandy
|
||||
new LatLng(50.44, -3.29),
|
||||
new LatLng(48.12,-3.29),
|
||||
new LatLng(48.12, -3.29),
|
||||
new LatLng(48.12, 3.70),
|
||||
new LatLng(50.44, 3.70),
|
||||
new LatLng(50.44, -3.29)
|
||||
],
|
||||
],
|
||||
"SinaiMap": [ // Sinai
|
||||
new LatLng(34.312222, 28.523333),
|
||||
new LatLng(25.946944, 28.523333),
|
||||
@ -161,36 +161,34 @@ export const defaultMapLayers = {
|
||||
maxZoom: 19,
|
||||
attribution: "Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Mapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
|
||||
},
|
||||
"USGS Topo": {
|
||||
urlTemplate: 'https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}',
|
||||
minZoom: 1,
|
||||
maxZoom: 14,
|
||||
attribution: 'Tiles courtesy of the <a href="https://usgs.gov/">U.S. Geological Survey</a>'
|
||||
},
|
||||
"OpenStreetMap Mapnik": {
|
||||
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
minZoom: 1,
|
||||
maxZoom: 20,
|
||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
},
|
||||
"OPENVKarte": {
|
||||
urlTemplate: 'https://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png',
|
||||
minZoom: 1,
|
||||
maxZoom: 20,
|
||||
attribution: 'Map <a href="https://memomaps.de/">memomaps.de</a> <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, map data © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||
},
|
||||
"Esri.DeLorme": {
|
||||
urlTemplate: 'https://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer/tile/{z}/{y}/{x}',
|
||||
minZoom: 1,
|
||||
maxZoom: 11,
|
||||
attribution: 'Tiles © Esri — Copyright: ©2012 DeLorme',
|
||||
},
|
||||
"CyclOSM": {
|
||||
urlTemplate: 'https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png',
|
||||
minZoom: 1,
|
||||
maxZoom: 20,
|
||||
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 Marianas Modern": [
|
||||
{
|
||||
urlTemplate: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
|
||||
minZoom: 1,
|
||||
maxZoom: 19
|
||||
},
|
||||
{
|
||||
urlTemplate: 'http://maps.dcsolympus.com/maps/marianas-modern/{z}/{x}/{y}.png',
|
||||
minNativeZoom: 1,
|
||||
maxNativeZoom: 13,
|
||||
minZoom: 1,
|
||||
maxZoom: 20
|
||||
},
|
||||
{
|
||||
urlTemplate: 'http://maps.dcsolympus.com/maps/marianas-modern/{z}/{x}/{y}.png',
|
||||
minNativeZoom: 14,
|
||||
maxNativeZoom: 16,
|
||||
minZoom: 14,
|
||||
maxZoom: 20,
|
||||
attribution: 'Map extracted by OzDeaDMeaT (DCS Olympus Team)'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
/* Map constants */
|
||||
@ -258,7 +256,7 @@ export const MAP_MARKER_CONTROLS: MapMarkerVisibilityControl[] = [{
|
||||
|
||||
export const IADSTypes = ["AAA", "SAM Site", "Radar (EWR)"];
|
||||
export const IADSDensities: { [key: string]: number } = { "AAA": 0.8, "SAM Site": 0.1, "Radar (EWR)": 0.05 };
|
||||
export const GROUND_UNIT_AIR_DEFENCE_REGEX:RegExp = /(\b(AAA|SAM|MANPADS?|[mM]anpads?)|[sS]tinger\b)/;
|
||||
export const GROUND_UNIT_AIR_DEFENCE_REGEX: RegExp = /(\b(AAA|SAM|MANPADS?|[mM]anpads?)|[sS]tinger\b)/;
|
||||
export const HIDE_GROUP_MEMBERS = "Hide group members when zoomed out";
|
||||
export const SHOW_UNIT_LABELS = "Show unit labels (L)";
|
||||
export const SHOW_UNITS_ENGAGEMENT_RINGS = "Show units threat range rings (Q)";
|
||||
|
||||
@ -50,7 +50,7 @@ export type MapMarkerVisibilityControl = {
|
||||
export class Map extends L.Map {
|
||||
#ID: string;
|
||||
#state: string;
|
||||
#layer: L.TileLayer | null = null;
|
||||
#layer: L.TileLayer | L.LayerGroup | null = null;
|
||||
#preventLeftClick: boolean = false;
|
||||
#leftClickTimer: number = 0;
|
||||
#deafultPanDelta: number = 100;
|
||||
@ -281,14 +281,14 @@ export class Map extends L.Map {
|
||||
|
||||
if (layerName in this.#mapLayers) {
|
||||
const layerData = this.#mapLayers[layerName];
|
||||
var options: L.TileLayerOptions = {
|
||||
attribution: layerData.attribution,
|
||||
minZoom: layerData.minZoom,
|
||||
maxZoom: layerData.maxZoom,
|
||||
minNativeZoom: layerData.minNativeZoom,
|
||||
maxNativeZoom: layerData.maxNativeZoom
|
||||
};
|
||||
this.#layer = new L.TileLayer(layerData.urlTemplate, options);
|
||||
if (layerData instanceof Array) {
|
||||
let layers = layerData.map((layer: any) => {
|
||||
return new L.TileLayer(layer.urlTemplate, layer);
|
||||
})
|
||||
this.#layer = new L.LayerGroup(layers);
|
||||
} else {
|
||||
this.#layer = new L.TileLayer(layerData.urlTemplate, layerData);
|
||||
}
|
||||
}
|
||||
|
||||
this.#layer?.addTo(this);
|
||||
|
||||
@ -468,6 +468,7 @@ export function createCheckboxOption(text: string, description: string, checked:
|
||||
"disabled": false,
|
||||
"name": "",
|
||||
"readOnly": false,
|
||||
"value": null,
|
||||
...options
|
||||
};
|
||||
var div = document.createElement("div");
|
||||
@ -476,10 +477,11 @@ export function createCheckboxOption(text: string, description: string, checked:
|
||||
label.title = description;
|
||||
var input = document.createElement("input");
|
||||
input.type = "checkbox";
|
||||
input.checked = checked;
|
||||
input.name = options.name;
|
||||
input.disabled = options.disabled;
|
||||
input.readOnly = options.readOnly;
|
||||
input.checked = checked;
|
||||
input.name = options.name;
|
||||
input.disabled = options.disabled;
|
||||
input.readOnly = options.readOnly;
|
||||
input.value = options.value;
|
||||
var span = document.createElement("span");
|
||||
span.innerText = text;
|
||||
label.appendChild(input);
|
||||
|
||||
@ -31,10 +31,11 @@ export abstract class UnitDataFile {
|
||||
headersHTML += `<th data-coalition="${coalition}">${coalition[0].toUpperCase() + coalition.substring(1)}</th>`;
|
||||
|
||||
const optionIsValid = this.data[category].hasOwnProperty(coalition);
|
||||
let checkboxHTML = createCheckboxOption(`${category}:${coalition}`, category, optionIsValid, () => { }, {
|
||||
let checkboxHTML = createCheckboxOption(``, category, optionIsValid, () => { }, {
|
||||
"disabled": !optionIsValid,
|
||||
"name": "category-coalition-selection",
|
||||
"readOnly": !optionIsValid
|
||||
"readOnly": !optionIsValid,
|
||||
"value" : `${category}:${coalition}`
|
||||
}).outerHTML;
|
||||
|
||||
if (optionIsValid)
|
||||
|
||||
@ -1,123 +0,0 @@
|
||||
======================= New log starting at Thu Feb 08 2024 21:55:39 GMT+0100 (Central European Standard Time) =======================
|
||||
Running in C:\Users\dpass\Documents\DCSOlympus\manager\javascripts
|
||||
Development build detected, skipping version checks...
|
||||
======================= New log starting at Thu Feb 08 2024 21:55:43 GMT+0100 (Central European Standard Time) =======================
|
||||
Running in C:\Users\dpass\Documents\DCSOlympus\manager\javascripts
|
||||
Found instance in C:\Users\dpass\Saved Games\DCS.openbeta, checking for Olympus
|
||||
Comparing Mods content in C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Comparing Scripts content in C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Error: ENOENT: no such file or directory, lstat 'C:\Users\dpass\Documents\DCSOlympus\scripts\OlympusHook.lua'
|
||||
at Object.realpathSync (node:fs:2655:7)
|
||||
at Object.realpathSync (node:electron/js2c/asar_bundle:2:5358)
|
||||
at Object.compareSync (C:\Users\dpass\Documents\DCSOlympus\manager\node_modules\dir-compare\build\src\index.js:44:88)
|
||||
at DCSInstance.checkInstallation (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\dcsinstance.js:196:41)
|
||||
at async DCSInstance.findInstances (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\dcsinstance.js:68:21)
|
||||
at async Manager.start (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\manager.js:97:29)
|
||||
at async C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\preload.js:267:5 {
|
||||
errno: -4058,
|
||||
syscall: 'lstat',
|
||||
code: 'ENOENT',
|
||||
path: 'C:\\Users\\dpass\\Documents\\DCSOlympus\\scripts\\OlympusHook.lua'
|
||||
}
|
||||
Differences found!
|
||||
Comparing Mods content in C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Development build detected, skipping version checks...
|
||||
Comparing Scripts content in C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Error: ENOENT: no such file or directory, lstat 'C:\Users\dpass\Documents\DCSOlympus\scripts\OlympusHook.lua'
|
||||
at Object.realpathSync (node:fs:2655:7)
|
||||
at Object.realpathSync (node:electron/js2c/asar_bundle:2:5358)
|
||||
at Object.compareSync (C:\Users\dpass\Documents\DCSOlympus\manager\node_modules\dir-compare\build\src\index.js:44:88)
|
||||
at DCSInstance.checkInstallation (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\dcsinstance.js:196:41)
|
||||
at async DCSInstance.reloadInstances (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\dcsinstance.js:38:13)
|
||||
at async Manager.setState (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\manager.js:725:9)
|
||||
at async menuPage.options.onShow (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\manager.js:165:17) {
|
||||
errno: -4058,
|
||||
syscall: 'lstat',
|
||||
code: 'ENOENT',
|
||||
path: 'C:\\Users\\dpass\\Documents\\DCSOlympus\\scripts\\OlympusHook.lua'
|
||||
}
|
||||
Differences found!
|
||||
Fixing Olympus in C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Deleting mod from C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Mod succesfully removed from C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Deleting hooks from C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Deleting C:\Users\dpass\Saved Games\DCS.openbeta\Scripts\Hooks\OlympusHook.lua
|
||||
C:\Users\dpass\Saved Games\DCS.openbeta\Scripts\Hooks\OlympusHook.lua does not exist, nothing to do
|
||||
Installing mod in C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Mod succesfully installed in C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
C:\Users\dpass\Documents\DCS Olympus backups\DCS.openbeta\databases
|
||||
Backup databases found, copying over
|
||||
Backup mods.lua found, copying over
|
||||
Installing hooks in C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
[Error: ENOENT: no such file or directory, lstat 'C:\Users\dpass\Documents\DCSOlympus\scripts\OlympusHook.lua'] {
|
||||
errno: -4058,
|
||||
code: 'ENOENT',
|
||||
syscall: 'lstat',
|
||||
path: 'C:\\Users\\dpass\\Documents\\DCSOlympus\\scripts\\OlympusHook.lua'
|
||||
}
|
||||
Comparing Mods content in C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Comparing Scripts content in C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Error: ENOENT: no such file or directory, lstat 'C:\Users\dpass\Documents\DCSOlympus\scripts\OlympusHook.lua'
|
||||
at Object.realpathSync (node:fs:2655:7)
|
||||
at Object.realpathSync (node:electron/js2c/asar_bundle:2:5358)
|
||||
at Object.compareSync (C:\Users\dpass\Documents\DCSOlympus\manager\node_modules\dir-compare\build\src\index.js:44:88)
|
||||
at DCSInstance.checkInstallation (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\dcsinstance.js:196:41)
|
||||
at async DCSInstance.reloadInstances (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\dcsinstance.js:38:13)
|
||||
at async Manager.setState (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\manager.js:725:9)
|
||||
at async Manager.onEditMenuClicked (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\manager.js:298:9) {
|
||||
errno: -4058,
|
||||
syscall: 'lstat',
|
||||
code: 'ENOENT',
|
||||
path: 'C:\\Users\\dpass\\Documents\\DCSOlympus\\scripts\\OlympusHook.lua'
|
||||
}
|
||||
Differences found!
|
||||
Comparing Mods content in C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Comparing Scripts content in C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Error: ENOENT: no such file or directory, lstat 'C:\Users\dpass\Documents\DCSOlympus\scripts\OlympusHook.lua'
|
||||
at Object.realpathSync (node:fs:2655:7)
|
||||
at Object.realpathSync (node:electron/js2c/asar_bundle:2:5358)
|
||||
at Object.compareSync (C:\Users\dpass\Documents\DCSOlympus\manager\node_modules\dir-compare\build\src\index.js:44:88)
|
||||
at DCSInstance.checkInstallation (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\dcsinstance.js:196:41)
|
||||
at async DCSInstance.reloadInstances (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\dcsinstance.js:38:13)
|
||||
at async Manager.setState (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\manager.js:725:9)
|
||||
at async Manager.onUninstallClicked (C:\Users\dpass\Documents\DCSOlympus\manager\javascripts\manager.js:580:9) {
|
||||
errno: -4058,
|
||||
syscall: 'lstat',
|
||||
code: 'ENOENT',
|
||||
path: 'C:\\Users\\dpass\\Documents\\DCSOlympus\\scripts\\OlympusHook.lua'
|
||||
}
|
||||
Differences found!
|
||||
Uninstalling Olympus from C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Deleting mod from C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Mod succesfully removed from C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Deleting hooks from C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Deleting C:\Users\dpass\Saved Games\DCS.openbeta\Scripts\Hooks\OlympusHook.lua
|
||||
C:\Users\dpass\Saved Games\DCS.openbeta\Scripts\Hooks\OlympusHook.lua does not exist, nothing to do
|
||||
Deleting JSON from C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Deleting C:\Users\dpass\Saved Games\DCS.openbeta\Config\olympus.json
|
||||
C:\Users\dpass\Saved Games\DCS.openbeta\Config\olympus.json does not exist, nothing to do
|
||||
Deleting ShortCuts from C:\Users\dpass\Saved Games\DCS.openbeta and desktop
|
||||
Deleting C:\Users\dpass\Saved Games\DCS.openbeta\DCS Olympus Server (DCS.openbeta).lnk
|
||||
C:\Users\dpass\Saved Games\DCS.openbeta\DCS Olympus Server (DCS.openbeta).lnk does not exist, nothing to do
|
||||
Deleting C:\Users\dpass\Saved Games\DCS.openbeta\DCS Olympus Client (DCS.openbeta).lnk
|
||||
C:\Users\dpass\Saved Games\DCS.openbeta\DCS Olympus Client (DCS.openbeta).lnk does not exist, nothing to do
|
||||
Deleting C:\Users\dpass\Desktop\DCS Olympus Server (DCS.openbeta).lnk
|
||||
C:\Users\dpass\Desktop\DCS Olympus Server (DCS.openbeta).lnk does not exist, nothing to do
|
||||
Deleting C:\Users\dpass\Desktop\DCS Olympus Client (DCS.openbeta).lnk
|
||||
C:\Users\dpass\Desktop\DCS Olympus Client (DCS.openbeta).lnk does not exist, nothing to do
|
||||
ShortCuts deleted from C:\Users\dpass\Saved Games\DCS.openbeta and desktop
|
||||
Olympus removed from C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
Installing hooks in C:\Users\dpass\Saved Games\DCS.openbeta
|
||||
An error occurred during installation: Error: ENOENT: no such file or directory, lstat 'C:\Users\dpass\Documents\DCSOlympus\scripts\OlympusHook.lua'
|
||||
======================= New log starting at Thu Feb 08 2024 21:58:08 GMT+0100 (Central European Standard Time) =======================
|
||||
Running in C:\Users\dpass\Documents\DCSOlympus\manager\javascripts
|
||||
Found instance in C:\Users\dpass\Saved Games\DCS.openbeta, checking for Olympus
|
||||
Development build detected, skipping version checks...
|
||||
======================= New log starting at Thu Feb 08 2024 21:59:00 GMT+0100 (Central European Standard Time) =======================
|
||||
Running in C:\Users\dpass\Documents\DCSOlympus\manager\javascripts
|
||||
Found instance in C:\Users\dpass\Saved Games\DCS.openbeta, checking for Olympus
|
||||
Development build detected, skipping version checks...
|
||||
======================= New log starting at Thu Feb 08 2024 21:59:52 GMT+0100 (Central European Standard Time) =======================
|
||||
Running in C:\Users\dpass\Documents\DCSOlympus\manager\javascripts
|
||||
Found instance in C:\Users\dpass\Saved Games\DCS.openbeta, checking for Olympus
|
||||
Development build detected, skipping version checks...
|
||||
16
olympus.json
16
olympus.json
@ -16,6 +16,22 @@
|
||||
"password": null
|
||||
},
|
||||
"additionalMaps": {
|
||||
"TestMap": [
|
||||
{
|
||||
"urlTemplate": "http://localhost:3000/maps/TestMap/{z}/{x}/{y}.png",
|
||||
"minNativeZoom": 1,
|
||||
"maxNativeZoom": 13,
|
||||
"minZoom": 1,
|
||||
"maxZoom": 20
|
||||
},
|
||||
{
|
||||
"urlTemplate": "http://localhost:3000/maps/TestMap/{z}/{x}/{y}.png",
|
||||
"minNativeZoom": 14,
|
||||
"maxNativeZoom": 17,
|
||||
"minZoom": 14,
|
||||
"maxZoom": 20
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
3
scripts/python/map_generator/.gitignore
vendored
Normal file
3
scripts/python/map_generator/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
Syria
|
||||
Test
|
||||
Caucasus
|
||||
12
scripts/python/map_generator/.vscode/launch.json
vendored
12
scripts/python/map_generator/.vscode/launch.json
vendored
@ -5,12 +5,20 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python: Current File",
|
||||
"name": "Main",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "main.py",
|
||||
"console": "integratedTerminal",
|
||||
"args": ["./configs/Caucasus/HighResolution.yml"]
|
||||
"args": ["-s", "-l", "1", "./configs/Test/MediumResolution.yml"]
|
||||
},
|
||||
{
|
||||
"name": "Convert",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "convert_to_jpg.py",
|
||||
"console": "integratedTerminal",
|
||||
"args": ["./Syria/tiles", "./Syria/jpg"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -1,15 +1,16 @@
|
||||
<?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>
|
||||
<name>Low resolution</name>
|
||||
<open>1</open>
|
||||
<gx:CascadingStyle kml:id="__managed_style_280E5494AE2F24E92C22">
|
||||
<Style>
|
||||
<IconStyle>
|
||||
<scale>1.2</scale>
|
||||
<Icon>
|
||||
<href>https://earth.google.com/earth/rpc/cc/icon?color=1976d2&id=2000&scale=4</href>
|
||||
<href>https://earth.google.com/earth/rpc/cc/icon?color=1976d2<![CDATA[&]]>id=2000<![CDATA[&]]>scale=4</href>
|
||||
</Icon>
|
||||
<hotSpot x="64" y="128" xunits="pixels" yunits="insetPixels"/>
|
||||
<hotSpot x="64" y="128" xunits="pixels" yunits="insetPixels"></hotSpot>
|
||||
</IconStyle>
|
||||
<LabelStyle>
|
||||
</LabelStyle>
|
||||
@ -24,14 +25,13 @@
|
||||
<displayMode>hide</displayMode>
|
||||
</BalloonStyle>
|
||||
</Style>
|
||||
</gx:CascadingStyle>
|
||||
<gx:CascadingStyle kml:id="__managed_style_1EB9027B622F24E92C22">
|
||||
</gx:CascadingStyle><gx:CascadingStyle kml:id="__managed_style_1EB9027B622F24E92C22">
|
||||
<Style>
|
||||
<IconStyle>
|
||||
<Icon>
|
||||
<href>https://earth.google.com/earth/rpc/cc/icon?color=1976d2&id=2000&scale=4</href>
|
||||
<href>https://earth.google.com/earth/rpc/cc/icon?color=1976d2<![CDATA[&]]>id=2000<![CDATA[&]]>scale=4</href>
|
||||
</Icon>
|
||||
<hotSpot x="64" y="128" xunits="pixels" yunits="insetPixels"/>
|
||||
<hotSpot x="64" y="128" xunits="pixels" yunits="insetPixels"></hotSpot>
|
||||
</IconStyle>
|
||||
<LabelStyle>
|
||||
</LabelStyle>
|
||||
@ -50,31 +50,35 @@
|
||||
<StyleMap id="__managed_style_0F57E9B9782F24E92C22">
|
||||
<Pair>
|
||||
<key>normal</key>
|
||||
<styleUrl>#__managed_style_1EB9027B622F24E92C22</styleUrl>
|
||||
<styleUrl>#failed0</styleUrl>
|
||||
</Pair>
|
||||
<Pair>
|
||||
<key>highlight</key>
|
||||
<styleUrl>#__managed_style_280E5494AE2F24E92C22</styleUrl>
|
||||
<styleUrl>#failed</styleUrl>
|
||||
</Pair>
|
||||
</StyleMap>
|
||||
<Style id="failed">
|
||||
</Style>
|
||||
<Style id="failed0">
|
||||
</Style>
|
||||
<Placemark id="0975D432582F24E92C1E">
|
||||
<name>Poligono senza titolo</name>
|
||||
<name>Low resolution</name>
|
||||
<LookAt>
|
||||
<longitude>37.25019544589698</longitude>
|
||||
<latitude>44.41771380726969</latitude>
|
||||
<altitude>-138.6844933247498</altitude>
|
||||
<heading>0</heading>
|
||||
<tilt>0</tilt>
|
||||
<gx:fovy>35</gx:fovy>
|
||||
<range>3831683.119853139</range>
|
||||
<altitudeMode>absolute</altitudeMode>
|
||||
<gx:fovy>35</gx:fovy>
|
||||
</LookAt>
|
||||
<styleUrl>#__managed_style_0F57E9B9782F24E92C22</styleUrl>
|
||||
<Polygon>
|
||||
<outerBoundaryIs>
|
||||
<LinearRing>
|
||||
<coordinates>
|
||||
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
|
||||
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.82458556682658,39.86622166029085,0 45.8381687868953,39.59275112715873,0 45.98912922607954,43.86743059613963,0 46.10445698500042,45.31544913665732,0 34.15598911357598,46.421850808793,0 32.46459319237173,45.67416695848307,0
|
||||
</coordinates>
|
||||
</LinearRing>
|
||||
</outerBoundaryIs>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
{
|
||||
'output_directory': '.\NTTR', # Where to save the output files
|
||||
'boundary_file': '.\configs\NTTR\boundary.kml', # Input kml file setting the boundary of the map to create
|
||||
'boundary_file': '.\configs\NTTR\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)]
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
<?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>MediumResolution.kml</name>
|
||||
<StyleMap id="m_ylw-pushpin">
|
||||
<Pair>
|
||||
<key>normal</key>
|
||||
<styleUrl>#s_ylw-pushpin</styleUrl>
|
||||
</Pair>
|
||||
<Pair>
|
||||
<key>highlight</key>
|
||||
<styleUrl>#s_ylw-pushpin_hl</styleUrl>
|
||||
</Pair>
|
||||
</StyleMap>
|
||||
<Style id="s_ylw-pushpin">
|
||||
<IconStyle>
|
||||
<scale>1.1</scale>
|
||||
<Icon>
|
||||
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
|
||||
</Icon>
|
||||
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
|
||||
</IconStyle>
|
||||
</Style>
|
||||
<Style id="s_ylw-pushpin_hl">
|
||||
<IconStyle>
|
||||
<scale>1.3</scale>
|
||||
<Icon>
|
||||
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
|
||||
</Icon>
|
||||
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
|
||||
</IconStyle>
|
||||
</Style>
|
||||
<Placemark>
|
||||
<name>Untitled Polygon</name>
|
||||
<styleUrl>#m_ylw-pushpin</styleUrl>
|
||||
<Polygon>
|
||||
<tessellate>1</tessellate>
|
||||
<outerBoundaryIs>
|
||||
<LinearRing>
|
||||
<coordinates>
|
||||
-114.934770823132,36.0279645610818,0 -114.8423692017325,36.14486669307939,0 -114.8222988019772,36.33833555517232,0 -115.1043264248258,36.37857716972037,0 -115.4103523219488,36.37739101654672,0 -115.4685702901508,36.22908230367275,0 -115.3526428993333,35.95526029180954,0 -115.070949845899,35.9300532859497,0 -114.934770823132,36.0279645610818,0
|
||||
</coordinates>
|
||||
</LinearRing>
|
||||
</outerBoundaryIs>
|
||||
</Polygon>
|
||||
</Placemark>
|
||||
</Document>
|
||||
</kml>
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
'output_directory': '.\Syria', # Where to save the output files
|
||||
'boundary_file': '.\configs\Syria\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)]
|
||||
}
|
||||
77
scripts/python/map_generator/configs/Syria/LowResolution.kml
Normal file
77
scripts/python/map_generator/configs/Syria/LowResolution.kml
Normal file
@ -0,0 +1,77 @@
|
||||
<?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>LowResolution.kml</name>
|
||||
<StyleMap id="msn_ylw-pushpin">
|
||||
<Pair>
|
||||
<key>normal</key>
|
||||
<styleUrl>#sn_ylw-pushpin</styleUrl>
|
||||
</Pair>
|
||||
<Pair>
|
||||
<key>highlight</key>
|
||||
<styleUrl>#sh_ylw-pushpin</styleUrl>
|
||||
</Pair>
|
||||
</StyleMap>
|
||||
<Style id="sh_ylw-pushpin">
|
||||
<IconStyle>
|
||||
<scale>1.3</scale>
|
||||
<Icon>
|
||||
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
|
||||
</Icon>
|
||||
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
|
||||
</IconStyle>
|
||||
<BalloonStyle>
|
||||
</BalloonStyle>
|
||||
<LineStyle>
|
||||
<color>ccffffff</color>
|
||||
</LineStyle>
|
||||
<PolyStyle>
|
||||
<color>ccffffff</color>
|
||||
</PolyStyle>
|
||||
</Style>
|
||||
<Style id="sn_ylw-pushpin">
|
||||
<IconStyle>
|
||||
<scale>1.1</scale>
|
||||
<Icon>
|
||||
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
|
||||
</Icon>
|
||||
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
|
||||
</IconStyle>
|
||||
<BalloonStyle>
|
||||
</BalloonStyle>
|
||||
<LineStyle>
|
||||
<color>ccffffff</color>
|
||||
</LineStyle>
|
||||
<PolyStyle>
|
||||
<color>ccffffff</color>
|
||||
</PolyStyle>
|
||||
</Style>
|
||||
<Placemark>
|
||||
<name>Untitled Polygon</name>
|
||||
<snippet></snippet>
|
||||
<styleUrl>#msn_ylw-pushpin</styleUrl>
|
||||
<MultiGeometry>
|
||||
<Polygon>
|
||||
<tessellate>1</tessellate>
|
||||
<outerBoundaryIs>
|
||||
<LinearRing>
|
||||
<coordinates>
|
||||
32.41977350619059,35.86470912083693,0 33.56465531835013,35.91338825318785,0 34.80465378369205,36.47063196055174,0 35.60801631485135,36.34864532719317,0 35.49398676174558,34.21289059062669,0 34.5206672018921,31.81057604629355,0 43.95360383704013,31.37489080200468,0 44.25820669948404,37.93208169895772,0 30.99244388115055,37.7086326320776,0 31.19610811985261,36.49739270534763,0 32.41977350619059,35.86470912083693,0
|
||||
</coordinates>
|
||||
</LinearRing>
|
||||
</outerBoundaryIs>
|
||||
</Polygon>
|
||||
<Polygon>
|
||||
<tessellate>1</tessellate>
|
||||
<outerBoundaryIs>
|
||||
<LinearRing>
|
||||
<coordinates>
|
||||
32.49633696227876,34.50684717795233,0 33.09849375950411,34.48604280166101,0 33.90493204321842,34.73753410158289,0 34.25924480309209,34.95889575718786,0 34.62649679333629,35.4114629095852,0 34.72182823714807,35.80074945874743,0 32.99109621791929,35.58668302906084,0 32.02791860799897,35.17891332024415,0 32.07780557518355,34.72891507299879,0 32.49633696227876,34.50684717795233,0
|
||||
</coordinates>
|
||||
</LinearRing>
|
||||
</outerBoundaryIs>
|
||||
</Polygon>
|
||||
</MultiGeometry>
|
||||
</Placemark>
|
||||
</Document>
|
||||
</kml>
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
'output_directory': '.\Syria', # Where to save the output files
|
||||
'boundary_file': '.\configs\Syria\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)]
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
<?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>MediumResolution.kml</name>
|
||||
<StyleMap id="msn_ylw-pushpin">
|
||||
<Pair>
|
||||
<key>normal</key>
|
||||
<styleUrl>#sn_ylw-pushpin</styleUrl>
|
||||
</Pair>
|
||||
<Pair>
|
||||
<key>highlight</key>
|
||||
<styleUrl>#sh_ylw-pushpin</styleUrl>
|
||||
</Pair>
|
||||
</StyleMap>
|
||||
<Style id="sh_ylw-pushpin">
|
||||
<IconStyle>
|
||||
<scale>1.3</scale>
|
||||
<Icon>
|
||||
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
|
||||
</Icon>
|
||||
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
|
||||
</IconStyle>
|
||||
<BalloonStyle>
|
||||
</BalloonStyle>
|
||||
<LineStyle>
|
||||
<color>ccffffff</color>
|
||||
</LineStyle>
|
||||
<PolyStyle>
|
||||
<color>73ffffff</color>
|
||||
</PolyStyle>
|
||||
</Style>
|
||||
<Style id="sn_ylw-pushpin">
|
||||
<IconStyle>
|
||||
<scale>1.1</scale>
|
||||
<Icon>
|
||||
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
|
||||
</Icon>
|
||||
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
|
||||
</IconStyle>
|
||||
<BalloonStyle>
|
||||
</BalloonStyle>
|
||||
<LineStyle>
|
||||
<color>ccffffff</color>
|
||||
</LineStyle>
|
||||
<PolyStyle>
|
||||
<color>73ffffff</color>
|
||||
</PolyStyle>
|
||||
</Style>
|
||||
<Placemark>
|
||||
<name>Untitled Polygon</name>
|
||||
<snippet></snippet>
|
||||
<styleUrl>#msn_ylw-pushpin</styleUrl>
|
||||
<MultiGeometry>
|
||||
<Polygon>
|
||||
<tessellate>1</tessellate>
|
||||
<outerBoundaryIs>
|
||||
<LinearRing>
|
||||
<coordinates>
|
||||
34.67142705790602,31.82805055444974,0 35.02181508963836,31.82750977829048,0 36.01467177481414,31.98675180910247,0 36.23127775126287,32.04983840957699,0 35.9582646803902,32.28158293849557,0 35.99098147339456,32.44310980472873,0 36.09260593928732,32.55565643757006,0 36.61524825014531,32.48930025627462,0 36.60745776125667,32.80133625144484,0 36.47504812245365,33.05297304144123,0 36.35141964104501,33.3083703333435,0 36.35264203669159,33.49386454528514,0 36.47877595475967,33.58571959973897,0 36.19517390228444,33.5997606321796,0 35.83522397638479,33.61896603596939,0 35.91132793661291,33.79098072794213,0 36.15374710260814,34.26719887569751,0 36.25809031624735,34.42504420256356,0 36.50878759959249,34.48069803556503,0 36.72702134605258,34.5389760993956,0 36.91155927128435,34.70573020374997,0 36.93709483596604,34.8575453420497,0 36.84204849159945,35.0188804802811,0 36.85909086701554,35.12582156419841,0 36.80631271949839,35.2023007921343,0 36.70610451487763,35.28868757265818,0 36.64736791080302,35.33884042510283,0 36.58285870081706,35.39379914572466,0 36.45913791605572,35.785089727734,0 36.53039600108779,35.94339777542626,0 36.67529798763221,36.16555606706433,0 37.19354340712839,36.10461671438422,0 37.30210041940058,36.1672896392403,0 37.26914131633509,36.28180024676594,0 37.10359257192819,36.30310271023975,0 37.00689884978731,36.29046216813635,0 36.76464397760902,36.31629718954267,0 36.58584728151514,36.41485334551175,0 36.43276721089462,36.51419040401807,0 36.55081614559194,36.74504433804638,0 36.64379067414161,36.89890120179727,0 36.65347158496201,37.09461289580837,0 36.5670026915683,37.32284183349276,0 36.18331192127231,37.44082203549982,0 35.69883621035919,37.46691398938869,0 35.10303034414353,37.32678813082578,0 34.73373550417249,37.10256441028478,0 34.54672782985384,36.94278408400594,0 33.82387226696957,36.35935429361331,0 34.00780785927724,36.27288227125526,0 34.71609500104023,36.80115830525168,0 35.31231838128146,36.54974589517469,0 35.56842677899099,36.54590520153501,0 36.03653930366797,36.90903074000526,0 36.19000487976967,36.62318809953995,0 35.80177667905849,36.33612053423278,0 35.99066329315939,35.96583287218078,0 35.69248548202563,35.56301025420937,0 35.92254840918157,35.19455893234127,0 35.98470152257408,34.47521931409463,0 35.58103018441831,34.26637735596449,0 35.05797659081225,32.82931778284781,0 34.96691044071693,32.82607518990291,0 34.67142705790602,31.82805055444974,0
|
||||
</coordinates>
|
||||
</LinearRing>
|
||||
</outerBoundaryIs>
|
||||
</Polygon>
|
||||
<Polygon>
|
||||
<tessellate>1</tessellate>
|
||||
<outerBoundaryIs>
|
||||
<LinearRing>
|
||||
<coordinates>
|
||||
32.87727021160179,34.63039920198953,0 32.89796765395525,34.54413238804481,0 33.0513805484542,34.53311613075366,0 33.06805771189331,34.58449144684735,0 33.30594035942946,34.68068961400989,0 33.60655009787457,34.7834317386,0 33.66048901593272,34.8393762347752,0 33.70608869593579,34.95372458806005,0 33.90513134812218,34.93229745061615,0 34.1071842570156,34.94799211011232,0 33.94596222366071,35.13090630203342,0 34.03291716899977,35.33217278671437,0 34.00480141824541,35.41895359888199,0 33.86414270927582,35.36764075672064,0 33.48955253703217,35.27107279823272,0 32.94309978357973,35.39409014047924,0 32.72304170959251,35.13465820902891,0 33.02509969669595,35.06722355440451,0 33.44152949697186,35.00493863785168,0 33.43618672640994,34.91604930651071,0 33.31998050412736,34.76455001947792,0 32.87980862276667,34.77470445862257,0 32.7489895060826,34.66006328417646,0 32.87727021160179,34.63039920198953,0
|
||||
</coordinates>
|
||||
</LinearRing>
|
||||
</outerBoundaryIs>
|
||||
</Polygon>
|
||||
<Polygon>
|
||||
<tessellate>1</tessellate>
|
||||
<outerBoundaryIs>
|
||||
<LinearRing>
|
||||
<coordinates>
|
||||
37.316751241333,36.13625362966467,0 37.4802212403484,35.98662494935605,0 37.69339926931355,35.87154344120644,0 38.14290686820461,35.77632913948397,0 38.5968154612054,35.78564003854654,0 38.89284932130154,35.8779076116201,0 39.07507331612245,35.86639415034433,0 39.20140789434584,35.82480100674179,0 39.2433581758882,35.91988707796679,0 39.14777729544377,36.06853895742726,0 39.05867321853522,36.17697296815244,0 38.94276312671954,36.17039388164508,0 38.82993809202534,36.02864047564345,0 38.7422129714303,35.90314522534125,0 38.64230099519071,35.87869415817486,0 38.35798797186277,35.94095820496009,0 38.13165022867805,35.95691377930053,0 37.93541220365955,36.10819502548598,0 37.67868372711114,36.15507075771183,0 37.36536807770124,36.17289309161047,0 37.316751241333,36.13625362966467,0
|
||||
</coordinates>
|
||||
</LinearRing>
|
||||
</outerBoundaryIs>
|
||||
</Polygon>
|
||||
</MultiGeometry>
|
||||
</Placemark>
|
||||
</Document>
|
||||
</kml>
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
'output_directory': '.\Syria', # Where to save the output files
|
||||
'boundary_file': '.\configs\Syria\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)]
|
||||
}
|
||||
1
scripts/python/map_generator/configs/Syria/airbases.json
Normal file
1
scripts/python/map_generator/configs/Syria/airbases.json
Normal file
File diff suppressed because one or more lines are too long
1
scripts/python/map_generator/configs/Syria/airbases.kml
Normal file
1
scripts/python/map_generator/configs/Syria/airbases.kml
Normal file
File diff suppressed because one or more lines are too long
48
scripts/python/map_generator/configs/Test/LowResolution.kml
Normal file
48
scripts/python/map_generator/configs/Test/LowResolution.kml
Normal file
@ -0,0 +1,48 @@
|
||||
<?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>LowResolution.kml</name>
|
||||
<StyleMap id="m_ylw-pushpin">
|
||||
<Pair>
|
||||
<key>normal</key>
|
||||
<styleUrl>#s_ylw-pushpin</styleUrl>
|
||||
</Pair>
|
||||
<Pair>
|
||||
<key>highlight</key>
|
||||
<styleUrl>#s_ylw-pushpin_hl</styleUrl>
|
||||
</Pair>
|
||||
</StyleMap>
|
||||
<Style id="s_ylw-pushpin">
|
||||
<IconStyle>
|
||||
<scale>1.1</scale>
|
||||
<Icon>
|
||||
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
|
||||
</Icon>
|
||||
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
|
||||
</IconStyle>
|
||||
</Style>
|
||||
<Style id="s_ylw-pushpin_hl">
|
||||
<IconStyle>
|
||||
<scale>1.3</scale>
|
||||
<Icon>
|
||||
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
|
||||
</Icon>
|
||||
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
|
||||
</IconStyle>
|
||||
</Style>
|
||||
<Placemark>
|
||||
<name>Untitled Polygon</name>
|
||||
<styleUrl>#m_ylw-pushpin</styleUrl>
|
||||
<Polygon>
|
||||
<tessellate>1</tessellate>
|
||||
<outerBoundaryIs>
|
||||
<LinearRing>
|
||||
<coordinates>
|
||||
36.39481280710839,34.53819230857288,0 37.1796694816022,34.53262792655826,0 37.16621384783188,34.96261757019548,0 36.40060390213443,34.96928234867986,0 36.39481280710839,34.53819230857288,0
|
||||
</coordinates>
|
||||
</LinearRing>
|
||||
</outerBoundaryIs>
|
||||
</Polygon>
|
||||
</Placemark>
|
||||
</Document>
|
||||
</kml>
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
'output_directory': '.\Test', # Where to save the output files
|
||||
'boundary_file': '.\configs\Test\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)]
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
<?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>MediumResolution.kml</name>
|
||||
<StyleMap id="m_ylw-pushpin">
|
||||
<Pair>
|
||||
<key>normal</key>
|
||||
<styleUrl>#s_ylw-pushpin</styleUrl>
|
||||
</Pair>
|
||||
<Pair>
|
||||
<key>highlight</key>
|
||||
<styleUrl>#s_ylw-pushpin_hl</styleUrl>
|
||||
</Pair>
|
||||
</StyleMap>
|
||||
<Style id="s_ylw-pushpin">
|
||||
<IconStyle>
|
||||
<scale>1.1</scale>
|
||||
<Icon>
|
||||
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
|
||||
</Icon>
|
||||
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
|
||||
</IconStyle>
|
||||
</Style>
|
||||
<Style id="s_ylw-pushpin_hl">
|
||||
<IconStyle>
|
||||
<scale>1.3</scale>
|
||||
<Icon>
|
||||
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
|
||||
</Icon>
|
||||
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels"/>
|
||||
</IconStyle>
|
||||
</Style>
|
||||
<Placemark>
|
||||
<name>Untitled Polygon</name>
|
||||
<styleUrl>#m_ylw-pushpin</styleUrl>
|
||||
<Polygon>
|
||||
<tessellate>1</tessellate>
|
||||
<outerBoundaryIs>
|
||||
<LinearRing>
|
||||
<coordinates>
|
||||
36.66523183398584,34.6677562111174,0 36.7942537132104,34.66437454529529,0 36.83169732011983,34.73421112648619,0 36.82035549501334,34.78872158568599,0 36.81876694797644,34.78938919967693,0 36.71678396314233,34.81175454434261,0 36.71358833183864,34.81177596936792,0 36.64080355603824,34.80367957661652,0 36.61013139556666,34.76382161679572,0 36.60930683760219,34.76054596757088,0 36.60928608362357,34.75792169295608,0 36.60927570797851,34.75660959986943,0 36.61934895519745,34.71456838542862,0 36.64380557289082,34.68164055942542,0 36.66523183398584,34.6677562111174,0
|
||||
</coordinates>
|
||||
</LinearRing>
|
||||
</outerBoundaryIs>
|
||||
</Polygon>
|
||||
</Placemark>
|
||||
</Document>
|
||||
</kml>
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
'output_directory': '.\Test', # Where to save the output files
|
||||
'boundary_file': '.\configs\Test\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)]
|
||||
}
|
||||
98
scripts/python/map_generator/convert_to_jpg.py
Normal file
98
scripts/python/map_generator/convert_to_jpg.py
Normal file
@ -0,0 +1,98 @@
|
||||
import sys
|
||||
import os
|
||||
import numpy
|
||||
|
||||
from PIL import Image
|
||||
from concurrent import futures
|
||||
from os import listdir
|
||||
from os.path import isfile, isdir, join
|
||||
|
||||
# global counters
|
||||
fut_counter = 0
|
||||
tot_futs = 0
|
||||
|
||||
def printProgressBar(iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', printEnd = "\r"):
|
||||
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
|
||||
filledLength = int(length * iteration // total)
|
||||
bar = fill * filledLength + '-' * (length - filledLength)
|
||||
print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd)
|
||||
# Print New Line on Complete
|
||||
if iteration == total:
|
||||
print()
|
||||
|
||||
def done_callback(fut):
|
||||
global fut_counter, tot_futs
|
||||
fut_counter += 1
|
||||
printProgressBar(fut_counter, tot_futs)
|
||||
|
||||
def remove_black_areas(im):
|
||||
data = numpy.array(im)
|
||||
red, green, blue = data.T
|
||||
|
||||
# If present, remove any "black" areas
|
||||
background_areas = (red < 10) & (blue < 10) & (green < 10)
|
||||
data[..., :][background_areas.T] = (221, 221, 221)
|
||||
|
||||
return Image.fromarray(data)
|
||||
|
||||
def convert_tiles(source, dest, tile):
|
||||
zoom = tile[0]
|
||||
X = tile[1]
|
||||
Y = tile[2]
|
||||
|
||||
if not os.path.exists(os.path.join(dest, str(zoom))):
|
||||
try:
|
||||
os.mkdir(os.path.join(dest, str(zoom)))
|
||||
except FileExistsError:
|
||||
# Ignore this error, it means one other thread has already created the folder
|
||||
pass
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
if not os.path.exists(os.path.join(dest, str(zoom), str(X))):
|
||||
try:
|
||||
os.mkdir(os.path.join(dest, str(zoom), str(X)))
|
||||
except FileExistsError:
|
||||
# Ignore this error, it means one other thread has already created the folder
|
||||
pass
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
remove_black_areas(Image.open(os.path.join(source, str(zoom), str(X), f"{Y}.png")).convert('RGB')).save(os.path.join(dest, str(zoom), str(X), f"{Y}.jpg"))
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print("Please provide a source and a destination folder")
|
||||
else:
|
||||
source = sys.argv[1]
|
||||
dest = sys.argv[2]
|
||||
|
||||
if not os.path.exists(dest):
|
||||
try:
|
||||
os.mkdir(dest)
|
||||
except FileExistsError:
|
||||
# Ignore this error, it means one other thread has already created the folder
|
||||
pass
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
print(f"Listing source tiles...")
|
||||
existing_tiles = []
|
||||
zooms = [int(f) for f in listdir(source) if isdir(join(source, f))]
|
||||
for zoom in zooms:
|
||||
Xs = [int(f) for f in listdir(join(source, str(zoom))) if isdir(join(source, str(zoom), f))]
|
||||
for X in Xs:
|
||||
Ys = [int(f.removesuffix(".png")) for f in listdir(os.path.join(source, str(zoom), str(X))) if isfile(join(source, str(zoom), str(X), f))]
|
||||
for Y in Ys:
|
||||
existing_tiles.append((zoom, X, Y))
|
||||
|
||||
print(f"{len(existing_tiles)} tiles will be converted")
|
||||
|
||||
# Merge the tiles with parallel thread execution
|
||||
with futures.ThreadPoolExecutor() as executor:
|
||||
print(f"Converting tiles to jpg...")
|
||||
print(f"Initializing exectuion pool")
|
||||
futs = [executor.submit(convert_tiles, source, dest, tile) for tile in existing_tiles]
|
||||
tot_futs = len(futs)
|
||||
fut_counter = 0
|
||||
[fut.add_done_callback(done_callback) for fut in futs]
|
||||
[fut.result() for fut in futures.as_completed(futs)]
|
||||
@ -1,84 +1,100 @@
|
||||
import sys
|
||||
import os
|
||||
import yaml
|
||||
import json
|
||||
import requests
|
||||
import argparse
|
||||
|
||||
from pyproj import Geod
|
||||
from fastkml import kml
|
||||
from shapely import wkt
|
||||
from datetime import timedelta
|
||||
|
||||
import map_generator
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='DCS Olympus map generator',
|
||||
description='This script allows to automatically generate maps from DCS World',
|
||||
epilog='Hit the DCS Olympus Discord for more information')
|
||||
|
||||
parser.add_argument('config', help='map configuration yaml file')
|
||||
parser.add_argument('-s', '--skip_screenshots', action='store_true', help='if screenshots are already present, this flag will cause the script to completely skip the screenshot loop.')
|
||||
parser.add_argument('-r', '--replace_screenshots', action='store_true', help='if screenshots are already present, this flag will cause the script to replace all screenshots, even those that already exist. Has no effect if -s or --skip_screenshots is present.')
|
||||
parser.add_argument('-l', '--final_level', type=int, default=1, help='if tiles are already present for the zoom level that the script will output, this number will instruct up to which zoom level tile merging will be run. Defaults to 1.')
|
||||
parser.add_argument('-f', '--screenshots_folder', help='if provided, will force the script to save the screenshots here. Defaults to output_directory/screenshots.')
|
||||
parser.add_argument('-t', '--tiles_folder', help='if provided, will force the script to save the tiles here. Defaults to output_directory/tiles.')
|
||||
parser.add_argument('-o', '--screenshots_only', action='store_true', help='if provided, the script will only run the screenshot acquisition algorithm.')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# 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:
|
||||
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)
|
||||
config_file = args.config
|
||||
if config_file is None:
|
||||
raise Exception("No configuration file provided as input. Please run script with -h argument for more info")
|
||||
|
||||
print("Screen parameters:")
|
||||
print(f"-> Screen width: {screen_config['width']}px")
|
||||
print(f"-> Screen height: {screen_config['height']}px")
|
||||
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)
|
||||
|
||||
map_config.update(vars(args))
|
||||
if map_config["screenshots_folder"] is None:
|
||||
map_config["screenshots_folder"] = os.path.join(map_config['output_directory'], "screenshots")
|
||||
if map_config["tiles_folder"] is None:
|
||||
map_config["tiles_folder"] = os.path.join(map_config['output_directory'], "tiles")
|
||||
|
||||
print("Screen parameters:")
|
||||
print(f"-> Screen width: {screen_config['width']}px")
|
||||
print(f"-> Screen height: {screen_config['height']}px")
|
||||
|
||||
print("Map parameters:")
|
||||
print(f"-> Output directory: {map_config['output_directory']}")
|
||||
print(f"-> Screenshots folder: {map_config['screenshots_folder']}")
|
||||
print(f"-> Tiles folder: {map_config['tiles_folder']}")
|
||||
print(f"-> Boundary file: {map_config['boundary_file']}")
|
||||
print(f"-> Zoom factor: {map_config['zoom_factor']}")
|
||||
|
||||
if 'geo_width' in map_config:
|
||||
print(f"-> Geo width: {map_config['geo_width']}NM")
|
||||
|
||||
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 = []
|
||||
area = 0
|
||||
for feature in k.features():
|
||||
for sub_feature in list(feature.features()):
|
||||
geo = sub_feature.geometry
|
||||
area += abs(geod.geometry_area_perimeter(wkt.loads(geo.wkt))[0])
|
||||
features.append(sub_feature)
|
||||
|
||||
print(f"Found {len(features)} features in the provided kml file")
|
||||
|
||||
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), '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: ")
|
||||
|
||||
print("Map parameters:")
|
||||
print(f"-> Output directory: {map_config['output_directory']}")
|
||||
print(f"-> Boundary file: {map_config['boundary_file']}")
|
||||
print(f"-> Zoom factor: {map_config['zoom_factor']}")
|
||||
|
||||
if 'geo_width' in map_config:
|
||||
print(f"-> Geo width: {map_config['geo_width']}NM")
|
||||
|
||||
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 = []
|
||||
area = 0
|
||||
for feature in k.features():
|
||||
for sub_feature in list(feature.features()):
|
||||
geo = sub_feature.geometry
|
||||
area += abs(geod.geometry_area_perimeter(wkt.loads(geo.wkt))[0])
|
||||
features.append(sub_feature)
|
||||
|
||||
print(f"Found {len(features)} features in the provided kml file")
|
||||
|
||||
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), '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']
|
||||
|
||||
tile_size = 256 * map_config['mpps'] # 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 * 0.15)} (hh:mm:ss)")
|
||||
input("Press enter to continue...")
|
||||
|
||||
map_generator.run(map_config, port)
|
||||
|
||||
|
||||
|
||||
map_config['mpps'] = float(map_config['geo_width']) * 1852 / screen_config['width']
|
||||
|
||||
tile_size = 256 * map_config['mpps'] # 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}")
|
||||
|
||||
map_generator.run(map_config, port)
|
||||
@ -6,6 +6,7 @@ import os
|
||||
import yaml
|
||||
import json
|
||||
import numpy
|
||||
import datetime
|
||||
|
||||
from fastkml import kml
|
||||
from shapely import wkt, Point
|
||||
@ -21,6 +22,7 @@ tot_futs = 0
|
||||
# constants
|
||||
C = 40075016.686 # meters, Earth equatorial circumference
|
||||
R = C / (2 * math.pi) # meters, Earth equatorial radius
|
||||
PUT_RETRIES = 10 # allowable number of retries for the PUT request
|
||||
|
||||
def deg_to_num(lat_deg, lon_deg, zoom):
|
||||
lat_rad = math.radians(lat_deg)
|
||||
@ -39,7 +41,7 @@ def num_to_deg(xtile, ytile, zoom):
|
||||
def compute_mpps(lat, z):
|
||||
return C * math.cos(math.radians(lat)) / math.pow(2, z + 8)
|
||||
|
||||
def printProgressBar(iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', printEnd = "\r"):
|
||||
def print_progress_bar(iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', printEnd = "\r"):
|
||||
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
|
||||
filledLength = int(length * iteration // total)
|
||||
bar = fill * filledLength + '-' * (length - filledLength)
|
||||
@ -51,19 +53,20 @@ def printProgressBar(iteration, total, prefix = '', suffix = '', decimals = 1, l
|
||||
def done_callback(fut):
|
||||
global fut_counter, tot_futs
|
||||
fut_counter += 1
|
||||
printProgressBar(fut_counter, tot_futs)
|
||||
print_progress_bar(fut_counter, tot_futs)
|
||||
|
||||
def extract_tiles(n, screenshots_XY, params):
|
||||
f = params['f']
|
||||
zoom = params['zoom']
|
||||
output_directory = params['output_directory']
|
||||
n_width = params['n_width']
|
||||
n_height = params['n_height']
|
||||
screenshots_folder = params['screenshots_folder']
|
||||
tiles_folder = params['tiles_folder']
|
||||
|
||||
XY = screenshots_XY[n]
|
||||
if (os.path.exists(os.path.join(output_directory, "screenshots", f"{f}_{n}_{zoom}.jpg"))):
|
||||
if (os.path.exists(os.path.join(screenshots_folder, f"{f}_{n}_{zoom}.jpg"))):
|
||||
# Open the source screenshot
|
||||
img = Image.open(os.path.join(output_directory, "screenshots", f"{f}_{n}_{zoom}.jpg"))
|
||||
img = Image.open(os.path.join(screenshots_folder, 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]
|
||||
@ -81,39 +84,37 @@ def extract_tiles(n, screenshots_XY, params):
|
||||
Y = Y_center - math.floor(n_height / 2) + row
|
||||
|
||||
# Save the tile
|
||||
if not os.path.exists(os.path.join(output_directory, "tiles", str(zoom), str(X))):
|
||||
if not os.path.exists(os.path.join(tiles_folder, str(zoom), str(X))):
|
||||
try:
|
||||
os.mkdir(os.path.join(output_directory, "tiles", str(zoom), str(X)))
|
||||
os.mkdir(os.path.join(tiles_folder, str(zoom), str(X)))
|
||||
except FileExistsError:
|
||||
# Ignore this error, it means one other thread has already created the folder
|
||||
continue
|
||||
pass
|
||||
except Exception as e:
|
||||
raise e
|
||||
img.crop(box).save(os.path.join(output_directory, "tiles", str(zoom), str(X), f"{Y}.jpg"))
|
||||
img.crop(box).convert('RGBA').save(os.path.join(tiles_folder, str(zoom), str(X), f"{Y}.png"))
|
||||
n += 1
|
||||
|
||||
else:
|
||||
raise Exception(f"{os.path.join(output_directory, 'screenshots', f'{f}_{n}_{zoom}.jpg')} missing")
|
||||
raise Exception(f"{os.path.join(screenshots_folder, f'{f}_{n}_{zoom}.jpg')} missing")
|
||||
|
||||
def merge_tiles(base_path, zoom, tile):
|
||||
X = tile[0]
|
||||
Y = tile[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)
|
||||
if os.path.exists(os.path.join(base_path, str(zoom - 1), str(X), f"{Y}.png")):
|
||||
dst = Image.open(os.path.join(base_path, str(zoom - 1), str(X), f"{Y}.png"))
|
||||
else:
|
||||
dst = Image.new('RGB', (256, 256), (221, 221, 221))
|
||||
dst = Image.new('RGBA', (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))
|
||||
im = make_background_transparent(im)
|
||||
dst.paste(im, (positions[i][0] * 128, positions[i][1] * 128))
|
||||
if os.path.exists(os.path.join(base_path, str(zoom), str(2*X + positions[i][0]), f"{2*Y + positions[i][1]}.png")):
|
||||
im = Image.open(os.path.join(base_path, str(zoom), str(2*X + positions[i][0]), f"{2*Y + positions[i][1]}.png")).resize((128, 128))
|
||||
dst.paste(im, (positions[i][0] * 128, positions[i][1] * 128), im)
|
||||
|
||||
# Create the output folder if it exists
|
||||
if not os.path.exists(os.path.join(base_path, str(zoom - 1), str(X))):
|
||||
@ -126,43 +127,147 @@ def merge_tiles(base_path, zoom, tile):
|
||||
raise e
|
||||
|
||||
# Save the image
|
||||
dst.convert('RGB').save(os.path.join(base_path, str(zoom - 1), str(X), f"{Y}.jpg"), quality=95)
|
||||
dst.save(os.path.join(base_path, str(zoom - 1), str(X), f"{Y}.png"), quality=98)
|
||||
|
||||
def compute_correction_factor(XY, n_width, n_height, map_config, zoom, screenshots_folder, port):
|
||||
# Take screenshots at the given position
|
||||
take_screenshot(XY, 0, 0, map_config, zoom, screenshots_folder, "calib", "ref", port)
|
||||
calib_ref = Image.open(os.path.join(screenshots_folder, f"calib_ref_{zoom}.jpg"))
|
||||
|
||||
# These calibration boxes are located at the edge of the interest region
|
||||
box1 = (calib_ref.width / 2 + n_width / 2 * 256 - 50, calib_ref.height / 2 - n_height / 2 * 256 + 10,
|
||||
calib_ref.width / 2 + n_width / 2 * 256 + 50, calib_ref.height / 2 + n_height / 2 * 256 - 10)
|
||||
box2 = (calib_ref.width / 2 - n_width / 2 * 256 - 50, calib_ref.height / 2 - n_height / 2 * 256 + 10,
|
||||
calib_ref.width / 2 - n_width / 2 * 256 + 50, calib_ref.height / 2 + n_height / 2 * 256 - 10)
|
||||
|
||||
def make_background_transparent(im):
|
||||
im.putalpha(255)
|
||||
data = numpy.array(im)
|
||||
red, green, blue, alpha = data.T
|
||||
box3 = (calib_ref.width / 2 - n_width / 2 * 256 + 10, calib_ref.height / 2 + n_height / 2 * 256 - 50,
|
||||
calib_ref.width / 2 + n_width / 2 * 256 - 10, calib_ref.height / 2 + n_height / 2 * 256 + 50)
|
||||
box4 = (calib_ref.width / 2 - n_width / 2 * 256 + 10, calib_ref.height / 2 - n_height / 2 * 256 - 50,
|
||||
calib_ref.width / 2 + n_width / 2 * 256 - 10, calib_ref.height / 2 - n_height / 2 * 256 + 50)
|
||||
|
||||
# If present, remove any "background" areas
|
||||
background_areas = (red == 221) & (blue == 221) & (green == 221)
|
||||
data[..., :][background_areas.T] = (0, 0, 0, 0) # make transparent
|
||||
# Check if there is enough variation at the calibration locations
|
||||
if compute_variation(calib_ref.crop(box1).convert('L')) < 30 or \
|
||||
compute_variation(calib_ref.crop(box3).convert('L')) < 30:
|
||||
return None # Not enough variation
|
||||
|
||||
return Image.fromarray(data)
|
||||
# Take screenshot east and south of it
|
||||
take_screenshot((XY[0] + n_width, XY[1]), 0, 0, map_config, zoom, screenshots_folder, "calib", "lng", port)
|
||||
take_screenshot((XY[0], XY[1] + n_height), 0, 0, map_config, zoom, screenshots_folder, "calib", "lat", port)
|
||||
calib_lat = Image.open(os.path.join(screenshots_folder, f"calib_lat_{zoom}.jpg"))
|
||||
calib_lng = Image.open(os.path.join(screenshots_folder, f"calib_lng_{zoom}.jpg"))
|
||||
|
||||
# Find the best correction factor to bring the two images to be equal on the longitude direction
|
||||
best_err = None
|
||||
best_delta_width = 0
|
||||
for delta_width in range(-15, 16):
|
||||
calib_box1 = calib_ref.resize((calib_ref.width + delta_width, calib_ref.height)).crop(box1).convert('L')
|
||||
calib_box2 = calib_lng.resize((calib_ref.width + delta_width, calib_ref.height)).crop(box2).convert('L')
|
||||
err = compute_difference(calib_box1, calib_box2)
|
||||
if best_err is None or err < best_err:
|
||||
best_delta_width = delta_width
|
||||
best_err = err
|
||||
|
||||
# Find the best correction factor to bring the two images to be equal on the latitude direction
|
||||
best_err = None
|
||||
best_delta_height = 0
|
||||
for delta_height in range(-15, 16):
|
||||
calib_box3 = calib_ref.resize((calib_ref.width, calib_ref.height + delta_height)).crop(box3).convert('L')
|
||||
calib_box4 = calib_lat.resize((calib_ref.width, calib_ref.height + delta_height)).crop(box4).convert('L')
|
||||
err = compute_difference(calib_box3, calib_box4)
|
||||
if best_err is None or err < best_err:
|
||||
best_delta_height = delta_height
|
||||
best_err = err
|
||||
|
||||
return (best_delta_width, best_delta_height)
|
||||
|
||||
def compute_difference(imageA, imageB):
|
||||
err = numpy.sum((numpy.array(imageA).astype('float') - numpy.array(imageB).astype('float')) ** 2)
|
||||
err /= float(imageA.width * imageA.height)
|
||||
return err
|
||||
|
||||
def compute_variation(imageA):
|
||||
min = numpy.min((numpy.array(imageA)))
|
||||
max = numpy.max((numpy.array(imageA)))
|
||||
return max - min
|
||||
|
||||
def take_screenshot(XY, n_width, n_height, map_config, zoom, screenshots_folder, f, n, port, correction = (0, 0)):
|
||||
# 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), 'mode': 'map'})
|
||||
|
||||
# Try to send the PUT request, up to PUT_RETRIES
|
||||
retries = PUT_RETRIES
|
||||
success = False
|
||||
while not success and retries > 0:
|
||||
try:
|
||||
r = requests.put(f'http://127.0.0.1:{port}', data = data)
|
||||
success = True
|
||||
except:
|
||||
retries -= 1
|
||||
time.sleep(0.5) # Wait for any error to clear
|
||||
|
||||
if success == False:
|
||||
raise Exception(f"Could not fulfill PUT request after {PUT_RETRIES} retries")
|
||||
|
||||
geo_data = json.loads(r.text)
|
||||
|
||||
time.sleep(0.2)
|
||||
|
||||
# Take and save screenshot. The response to the put request contains data, among which there is the north rotation at that point.
|
||||
screenshot = pyautogui.screenshot()
|
||||
|
||||
# Scale the screenshot to account for Mercator Map Deformation
|
||||
lat1, lng1 = num_to_deg(XY[0], XY[1], zoom)
|
||||
lat2, lng2 = num_to_deg(XY[0] + 1, XY[1] + 1, zoom)
|
||||
|
||||
deltaLat = abs(lat2 - lat1)
|
||||
deltaLng = abs(lng2 - lng1)
|
||||
|
||||
# Compute the height and width each tile should have
|
||||
m_height = math.radians(deltaLat) * R
|
||||
m_width = math.radians(deltaLng) * R * math.cos(math.radians(lat1))
|
||||
|
||||
# Compute the height and width the tile has
|
||||
s_height = map_config['mpps'] * 256
|
||||
s_width = map_config['mpps'] * 256
|
||||
|
||||
# Compute the scaling required to achieve that
|
||||
sx = s_width / m_width
|
||||
sy = s_height / m_height
|
||||
|
||||
# Rotate, resize and save the screenshot
|
||||
screenshot.rotate(math.degrees(geo_data['northRotation'])).resize((int(sx * screenshot.width) + correction[0], int(sy * screenshot.height)+ correction[1] )).save(os.path.join(screenshots_folder, f"{f}_{n}_{zoom}.jpg"), quality=98)
|
||||
|
||||
def run(map_config, port):
|
||||
global tot_futs, fut_counter
|
||||
|
||||
print("Script start time: ", datetime.datetime.now())
|
||||
with open('configs/screen_properties.yml', 'r') as sp:
|
||||
screen_config = yaml.safe_load(sp)
|
||||
|
||||
# Create output folders
|
||||
output_directory = map_config['output_directory']
|
||||
if not os.path.exists(output_directory):
|
||||
os.mkdir(output_directory)
|
||||
if not os.path.exists(map_config['tiles_folder']):
|
||||
os.makedirs(map_config['tiles_folder'])
|
||||
|
||||
skip_screenshots = False
|
||||
if not os.path.exists(os.path.join(output_directory, "screenshots")):
|
||||
os.mkdir(os.path.join(output_directory, "screenshots"))
|
||||
if not os.path.exists(os.path.join(map_config['screenshots_folder'])):
|
||||
skip_screenshots = False
|
||||
replace_screenshots = True
|
||||
os.makedirs(os.path.join(map_config['screenshots_folder']))
|
||||
else:
|
||||
skip_screenshots = (input("Raw screenshots already found for this config, do you want to skip directly to tiles extraction? Enter y to skip: ") == "y")
|
||||
|
||||
if not os.path.exists(os.path.join(output_directory, "tiles")):
|
||||
os.mkdir(os.path.join(output_directory, "tiles"))
|
||||
skip_screenshots = map_config['skip_screenshots']
|
||||
replace_screenshots = map_config['replace_screenshots']
|
||||
|
||||
# Compute the optimal zoom level
|
||||
usable_width = screen_config['width'] - 400 # Keep a margin around the center
|
||||
usable_height = screen_config['height'] - 400 # Keep a margin around the center
|
||||
|
||||
existing_zoom_levels = [int(f) for f in listdir(os.path.join(map_config["tiles_folder"])) if isdir(join(map_config["tiles_folder"], f))]
|
||||
if len(existing_zoom_levels) == 0:
|
||||
final_level = 1
|
||||
else:
|
||||
final_level = max(existing_zoom_levels)
|
||||
|
||||
with open(map_config['boundary_file'], 'rt', encoding="utf-8") as bp:
|
||||
# Read the config file
|
||||
doc = bp.read()
|
||||
@ -218,58 +323,36 @@ def run(map_config, port):
|
||||
print(f"Feature {f} of {len(features)}, {len(screenshots_XY)} screenshots will be taken")
|
||||
|
||||
# Start looping
|
||||
correction = None
|
||||
if not skip_screenshots:
|
||||
print(f"Feature {f} of {len(features)}, taking screenshots...")
|
||||
n = 0
|
||||
for XY in screenshots_XY:
|
||||
# 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), 'mode': 'map'})
|
||||
r = requests.put(f'http://127.0.0.1:{port}', data = data)
|
||||
|
||||
geo_data = json.loads(r.text)
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
# Take and save screenshot. The response to the put request contains data, among which there is the north rotation at that point.
|
||||
screenshot = pyautogui.screenshot()
|
||||
|
||||
# Scale the screenshot to account for Mercator Map Deformation
|
||||
lat1, lng1 = num_to_deg(XY[0], XY[1], zoom)
|
||||
lat2, lng2 = num_to_deg(XY[0] + 1, XY[1] + 1, zoom)
|
||||
|
||||
deltaLat = abs(lat2 - lat1)
|
||||
deltaLng = abs(lng2 - lng1)
|
||||
|
||||
# Compute the height and width the screenshot should have
|
||||
m_height = math.radians(deltaLat) * R * n_height
|
||||
m_width = math.radians(deltaLng) * R * math.cos(math.radians(lat1)) * n_width
|
||||
|
||||
# Compute the height and width the screenshot has
|
||||
s_height = map_config['mpps'] * 256 * n_height
|
||||
s_width = map_config['mpps'] * 256 * n_width
|
||||
if not os.path.exists(os.path.join(map_config['screenshots_folder'], f"{f}_{n}_{zoom}.jpg")) or replace_screenshots:
|
||||
if n % 10 == 0 or correction is None:
|
||||
new_correction = compute_correction_factor(XY, n_width, n_height, map_config, zoom, map_config['screenshots_folder'], port)
|
||||
if new_correction is not None:
|
||||
correction = new_correction
|
||||
take_screenshot(XY, n_width, n_height, map_config, zoom, map_config['screenshots_folder'], f, n, port, correction if correction is not None else (0, 0))
|
||||
|
||||
# Compute the scaling required to achieve that
|
||||
sx = s_width / m_width
|
||||
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}_{zoom}.jpg"), quality=95)
|
||||
|
||||
printProgressBar(n + 1, len(screenshots_XY))
|
||||
print_progress_bar(n + 1, len(screenshots_XY))
|
||||
n += 1
|
||||
|
||||
if map_config["screenshots_only"]:
|
||||
return
|
||||
|
||||
########### Extract the tiles
|
||||
if not os.path.exists(os.path.join(output_directory, "tiles", str(zoom))):
|
||||
os.mkdir(os.path.join(output_directory, "tiles", str(zoom)))
|
||||
print("Tiles extraction starting at: ", datetime.datetime.now())
|
||||
if not os.path.exists(os.path.join(map_config["tiles_folder"], str(zoom))):
|
||||
os.mkdir(os.path.join(map_config["tiles_folder"], str(zoom)))
|
||||
|
||||
params = {
|
||||
"f": f,
|
||||
"zoom": zoom,
|
||||
"output_directory": output_directory,
|
||||
"n_width": n_width,
|
||||
"n_height": n_height,
|
||||
"screenshots_folder": map_config['screenshots_folder'],
|
||||
"tiles_folder": map_config['tiles_folder']
|
||||
}
|
||||
|
||||
# Extract the tiles with parallel thread execution
|
||||
@ -285,12 +368,16 @@ def run(map_config, port):
|
||||
print(f"Feature {f} of {len(features)} completed!")
|
||||
f += 1
|
||||
|
||||
if zoom <= final_level:
|
||||
final_level = map_config['final_level']
|
||||
|
||||
########### Assemble tiles to get lower zoom levels
|
||||
for current_zoom in range(zoom, 8, -1):
|
||||
Xs = [int(d) for d in listdir(os.path.join(output_directory, "tiles", str(current_zoom))) if isdir(join(output_directory, "tiles", str(current_zoom), d))]
|
||||
print("Tiles merging start time: ", datetime.datetime.now())
|
||||
for current_zoom in range(zoom, final_level, -1):
|
||||
Xs = [int(d) for d in listdir(os.path.join(map_config["tiles_folder"], str(current_zoom))) if isdir(join(map_config["tiles_folder"], str(current_zoom), d))]
|
||||
existing_tiles = []
|
||||
for X in Xs:
|
||||
Ys = [int(f.removesuffix(".jpg")) for f in listdir(os.path.join(output_directory, "tiles", str(current_zoom), str(X))) if isfile(join(output_directory, "tiles", str(current_zoom), str(X), f))]
|
||||
Ys = [int(f.removesuffix(".png")) for f in listdir(os.path.join(map_config["tiles_folder"], str(current_zoom), str(X))) if isfile(join(map_config["tiles_folder"], str(current_zoom), str(X), f))]
|
||||
for Y in Ys:
|
||||
existing_tiles.append((X, Y))
|
||||
|
||||
@ -298,20 +385,22 @@ def run(map_config, port):
|
||||
for tile in existing_tiles:
|
||||
if (int(tile[0] / 2), int(tile[1] / 2)) not in tiles_to_produce:
|
||||
tiles_to_produce.append((int(tile[0] / 2), int(tile[1] / 2)))
|
||||
|
||||
|
||||
# Merge the tiles with parallel thread execution
|
||||
with futures.ThreadPoolExecutor() as executor:
|
||||
print(f"Merging tiles for zoom level {current_zoom - 1}...")
|
||||
|
||||
if not os.path.exists(os.path.join(output_directory, "tiles", str(current_zoom - 1))):
|
||||
os.mkdir(os.path.join(output_directory, "tiles", str(current_zoom - 1)))
|
||||
if not os.path.exists(os.path.join(map_config["tiles_folder"], str(current_zoom - 1))):
|
||||
os.mkdir(os.path.join(map_config["tiles_folder"], str(current_zoom - 1)))
|
||||
|
||||
futs = [executor.submit(merge_tiles, os.path.join(output_directory, "tiles"), current_zoom, tile) for tile in tiles_to_produce]
|
||||
futs = [executor.submit(merge_tiles, os.path.join(map_config["tiles_folder"]), current_zoom, tile) for tile in tiles_to_produce]
|
||||
tot_futs = len(futs)
|
||||
fut_counter = 0
|
||||
[fut.add_done_callback(done_callback) for fut in futs]
|
||||
[fut.result() for fut in futures.as_completed(futs)]
|
||||
|
||||
print("Script end time: ", datetime.datetime.now())
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user