Skip to content

Commit

Permalink
Add first verson of tiles download client
Browse files Browse the repository at this point in the history
  • Loading branch information
Didosa committed Dec 9, 2023
1 parent b7b87f0 commit 8f87660
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 4 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -359,3 +359,5 @@ healthchecksdb
/PiLotSensors/Properties/launchSettings.json
/PiLotLiveClient/Properties/launchSettings.json
/PiLotBackupAPI/config/config.*.json
/PiLotTilesDownloader/Properties/launchSettings.json
/.gitignore
6 changes: 6 additions & 0 deletions PiLot.sln
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sql", "sql", "{B438012B-443
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PiLotGPIO", "PiLotGPIO\PiLotGPIO.csproj", "{5519B99B-064C-4271-A4B8-73100EEA7386}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PiLotTilesDownloader", "PiLotTilesDownloader\PiLotTilesDownloader.csproj", "{A1E25012-96D1-49AE-9E13-119B4239FB0D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -208,6 +210,10 @@ Global
{5519B99B-064C-4271-A4B8-73100EEA7386}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5519B99B-064C-4271-A4B8-73100EEA7386}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5519B99B-064C-4271-A4B8-73100EEA7386}.Release|Any CPU.Build.0 = Release|Any CPU
{A1E25012-96D1-49AE-9E13-119B4239FB0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1E25012-96D1-49AE-9E13-119B4239FB0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1E25012-96D1-49AE-9E13-119B4239FB0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1E25012-96D1-49AE-9E13-119B4239FB0D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
26 changes: 22 additions & 4 deletions PiLotConfig/Tiles/TilesConfigReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
namespace PiLot.Config {

/// <summary>
/// Helper for readind TileSources. We use a singleton
/// Helper for reading TileSources. We use a singleton
/// pattern in order to minimize read/write effort to read configs.
/// </summary>
public class TilesConfigReader {

public const String CONFIGFILENAME = "tileSources.json";

private string configFilePath = null;
private Dictionary<String, TileSource> tileSources = null;
private static TilesConfigReader instance = null;

Expand All @@ -29,6 +30,17 @@ private TilesConfigReader() {
this.ReadTileSources();
}

/// <summary>
/// Allows to create a config reader which does not use the config file in the
/// application, but any other config file. Can be useful for applications that
/// want to access the APIs tile sources.
/// </summary>
/// <param name="pConfigFilePath">An absolute file path</param>
public TilesConfigReader(String pConfigFilePath) {
this.configFilePath = pConfigFilePath;
this.ReadTileSources();
}

/// <summary>
/// Returns the current instance of the TileHelper, implementing the singleton pattern
/// </summary>
Expand Down Expand Up @@ -77,13 +89,19 @@ public static void ReloadConfig() {
#region private methods

/// <summary>
/// Reads the tileSources configs from the tileSources.json in App_Data
/// Reads the tileSources configs from the tileSources.json, or
/// from the config file that has been passed to the constructor.
/// </summary>
private void ReadTileSources() {
this.tileSources = new Dictionary<string, TileSource>();
String configPath;
if(this.configFilePath == null) {
String configRoot = ConfigHelper.GetConfigDirectory();
configPath = Path.Combine(configRoot, CONFIGFILENAME);
} else {
configPath = this.configFilePath;
}
List<TileSource> tileSourcesList = null;
String configRoot = ConfigHelper.GetConfigDirectory();
String configPath = Path.Combine(configRoot, CONFIGFILENAME);
Logger.Log("TileSources configPath is {0}", configPath, LogLevels.DEBUG);
if (File.Exists(configPath)) {
try {
Expand Down
16 changes: 16 additions & 0 deletions PiLotTilesDownloader/PiLotTilesDownloader.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\PiLotConfig\PiLotConfig.csproj" />
<ProjectReference Include="..\PiLotDataFiles\PiLotDataFiles.csproj" />
<ProjectReference Include="..\PiLotModel\PiLotModel.csproj" />
</ItemGroup>

</Project>
169 changes: 169 additions & 0 deletions PiLotTilesDownloader/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
(c) 2023 Röthenmund Consulting GmbH
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
The full license text is available at https://github.com/RCgmbh/PiLot/blob/master/LICENSE.
*/
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

using PiLot.Config;
using PiLot.Data.Files;
using PiLot.Model.Tiles;

namespace PiLot.TilesDownloader {

class Program {

private const Int32 PAUSEMS = 5000;
private const Int32 PAUSEAFTER = 10;
private const Int32 MAXAGEDAYS = 365;

private static String configFilePath;
private static TileSource[] tileSources;
private static TileDataConnector tileDataConnector;
private static HttpClient httpClient;
private static Random random;

/// <summary>
/// Usage: ./PiLot.TilesDownloader
/// </summary>
/// <param name="args">The path to the tileSources config must be passed</param>
static async Task Main(string[] args) {
if (
Program.ReadParameters(args)
&& Program.LoadTileSources()
){
await Program.StartTilesDownload();
}
Console.ReadKey();
}

private static Boolean ReadParameters(String[] args) {
Boolean result;
if(args.Length != 1) {
WriteError("The config file path must be passed as parameter.");
result = false;
} else {
configFilePath = args[0];
result = true;
}
return result;
}

private static Boolean LoadTileSources() {
Boolean result;
if (File.Exists(Program.configFilePath)) {
try {
Program.tileSources = new TilesConfigReader(Program.configFilePath).GetAllTileSources();
if(Program.tileSources.Length > 0) {
result = true;
} else {
Program.WriteError("The config file does not contain any tile sources");
result = false;
}

} catch (Exception ex) {
Program.WriteError(ex.Message);
result = false;
}
} else {
Program.WriteError($"config file not found at {Program.configFilePath}");
result = false;
}
return result;
}

private static async Task StartTilesDownload() {
Program.random = new Random();
Program.tileDataConnector = TileDataConnector.Instance;
Program.PrepareHttpClient();
await Program.DownloadRandomTiles();
}

private static void PrepareHttpClient() {
Program.httpClient = new HttpClient();
Program.httpClient.DefaultRequestHeaders.Add("Sec-Fetch-Dest", "image");
Program.httpClient.DefaultRequestHeaders.Add("Sec-Fetch-Mode", "no-cors");
Program.httpClient.DefaultRequestHeaders.Add("Sec-Fetch-Site", "cross-site");
Program.httpClient.DefaultRequestHeaders.Add("User-Agent", "PiLot map tiles download service. See github.com/RCgmbh.");
}


private static async Task DownloadRandomTiles() {
DateTime maxChangeDate = DateTime.UtcNow.AddDays(Program.MAXAGEDAYS * -1);
Int32 pauseCounter = 0;
while (true) {
TileSource tileSource = Program.tileSources[Program.random.Next(0, Program.tileSources.Length)];
String tileSourceRoot = tileSource.LocalPath.Substring(0, tileSource.LocalPath.IndexOf("{"));
DirectoryInfo rootDir = new DirectoryInfo(tileSourceRoot);
DirectoryInfo zoomDir = Program.GetRandomDirectory(rootDir);
DirectoryInfo xDir = Program.GetRandomDirectory(zoomDir);
if (xDir != null && xDir.Exists) {
foreach (FileInfo aFile in xDir.GetFiles().Where(f => f.LastWriteTimeUtc < maxChangeDate)) {
String y = aFile.Name.Substring(0, aFile.Name.Length - aFile.Extension.Length);
Console.WriteLine($" {y}: {aFile.FullName}");
await Program.DownloadTile(tileSource, zoomDir.Name, xDir.Name, y);
pauseCounter++;
if(pauseCounter >= PAUSEAFTER) {
pauseCounter = 0;
Thread.Sleep(Program.PAUSEMS);
}
}
}
}
}

private static DirectoryInfo GetRandomDirectory(DirectoryInfo pParent) {
DirectoryInfo result = null;
if(pParent != null && pParent.Exists) {
DirectoryInfo[] children = pParent.GetDirectories();
if (children.Length > 0) {
result = children[Program.random.Next(0, children.Length)];
}
}
return result;
}

private static async Task DownloadTile(TileSource pTileSource, String pZ, String pX, String pY) {
if (
Int32.TryParse(pZ, out Int32 z)
&& Int32.TryParse(pX, out Int32 x)
&& Int32.TryParse(pY, out Int32 y)
) {
String url = pTileSource.OnlineUrl
.Replace("{s}", "a")
.Replace("{z}", pZ)
.Replace("{x}", pX)
.Replace("{y}", pY);
HttpResponseMessage response = await Program.httpClient.GetAsync(url);
if (response.StatusCode == System.Net.HttpStatusCode.OK) {

Byte[] bytes = await response.Content.ReadAsByteArrayAsync();
TileDataConnector.SaveResults saveResult = Program.tileDataConnector.SaveTile(bytes, pTileSource, z, x, y);
if(saveResult != TileDataConnector.SaveResults.Ok) {
Program.WriteError($"Error saving tile from {url}. Result: {saveResult}");
}
} else {
Program.WriteError($"Status code {response.StatusCode} for {url}");
}
}
}

private static void WriteError(String pMessage) {
ConsoleColor defaultColor = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(pMessage);
Console.ForegroundColor = defaultColor;
}
}
}

0 comments on commit 8f87660

Please sign in to comment.