├── icon.png
├── src
└── SRTM
│ ├── ISRTMDataCell.cs
│ ├── Sources
│ ├── FuncSource.cs
│ ├── SourceHelpers.cs
│ ├── NASA
│ │ └── NASASource.cs
│ └── USGS
│ │ └── USGSSource.cs
│ ├── SRTM.csproj
│ ├── EmptySRTMDataCell.cs
│ ├── ISRTMSource.cs
│ ├── ISRTMData.cs
│ ├── SRTMDataCell.cs
│ ├── SRTMData.cs
│ └── Logging
│ └── LibLog.cs
├── test
└── SRTM.Tests.Functional
│ ├── SRTM.Tests.Functional.csproj
│ └── Program.cs
├── .gitignore
├── .github
└── workflows
│ ├── build.yml
│ └── release.yml
├── LICENSE.md
├── SRTM.sln
└── README.md
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/itinero/srtm/HEAD/icon.png
--------------------------------------------------------------------------------
/src/SRTM/ISRTMDataCell.cs:
--------------------------------------------------------------------------------
1 | namespace SRTM
2 | {
3 | public interface ISRTMDataCell
4 | {
5 | int Latitude { get; }
6 |
7 | int Longitude { get; }
8 |
9 | int? GetElevation(double latitude, double longitude);
10 |
11 | double? GetElevationBilinear(double latitude, double longitude);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/SRTM.Tests.Functional/SRTM.Tests.Functional.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net5.0
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/SRTM/Sources/FuncSource.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace SRTM.Sources
4 | {
5 | internal sealed class FuncSource : ISRTMSource
6 | {
7 | private readonly Func<(string path, string name), bool> _getMissingCell;
8 |
9 | public FuncSource(Func<(string path, string name), bool> getMissingCell)
10 | {
11 | _getMissingCell = getMissingCell;
12 | }
13 |
14 | public bool GetMissingCell(string path, string name)
15 | {
16 | return _getMissingCell((path, name));
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | [Oo]bj
2 | [Bb]in
3 | *.user
4 | *.suo
5 | *.[Cc]ache
6 | *.bak
7 | *.ncb
8 | *.log
9 | *.DS_Store
10 | [Tt]humbs.db
11 | _ReSharper.*
12 | *.resharper
13 | Ankh.NoLoad
14 | Core/Output*/
15 | UI/Output*/
16 | UI/WinForms/Output*/
17 | Data/Oracle/Output*/
18 | Data/PostgreSQL/Output*/
19 | Data/Redis/Output*/
20 | Data/SQLServer/Output*/
21 | Data/SQLite/Output*/
22 | Core/OsmSharp.UnitTests/test-results*/
23 | *.userprefs
24 | Output*/
25 | OutputAndroid*/
26 | OutputWindowsPhone*/
27 | *.psess
28 | test-results*/
29 | *.vsp
30 | .DotSettings
31 | *.vspx
32 | *.patch
33 | TestResults*/
34 |
35 | #Intellij cache
36 | .idea/
37 |
38 | packages/*
39 | !packages/repositories.config
40 | .vs/*
41 |
42 | TestResult*.xml
43 | *.routerdb
44 | *.osm.pbf
45 |
46 | *.shp
47 | *.dbf
48 | *.shx
49 | *.shx
50 | *.hgt.zip
51 | srtm-cache*/
52 |
53 |
54 | islands_*.geojson
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | branches: [ develop ]
6 |
7 | jobs:
8 | build-and-test:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v2
14 | with:
15 | submodules: 'true'
16 | - name: Setup .NET Core 5.
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 5.0.101
20 | - name: Restore packages.
21 | run: dotnet restore
22 | - name: Build all projects.
23 | run: dotnet build --configuration Release --no-restore
24 | - name: Nuget Pack
25 | run: dotnet pack -c release
26 | working-directory: ./src/SRTM/
27 | - name: Nuget push
28 | run: dotnet nuget push **/*.nupkg --skip-duplicate -k ${{ secrets.GITHUB_TOKEN }} -s https://nuget.pkg.github.com/itiner/index.json
29 | working-directory: ./src/
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release to nuget
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 |
7 | jobs:
8 | build-and-test:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - uses: actions/checkout@v2
14 | with:
15 | submodules: 'true'
16 | - name: Setup .NET Core 5.
17 | uses: actions/setup-dotnet@v1
18 | with:
19 | dotnet-version: 5.0.101
20 | - name: Restore packages.
21 | run: dotnet restore
22 | - name: Build all projects.
23 | run: dotnet build --configuration Release --no-restore
24 | - name: Nuget Pack
25 | run: dotnet pack -c release
26 | working-directory: ./src/SRTM/
27 | - name: Nuget push
28 | run: dotnet nuget push **/*.nupkg --skip-duplicate -k ${{ secrets.NUGET_TOKEN }} -s https://api.nuget.org/v3/index.json
29 | working-directory: ./src/SRTM/
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Ben Abelshausen, Alpine Chough Software
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/src/SRTM/SRTM.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Library
5 | netstandard2.0
6 |
7 |
8 | latest
9 |
10 |
11 |
12 | LIBLOG_PORTABLE
13 | 0.0.7
14 | Ben Abelshausen
15 | Itinero
16 | A library to read SRTM data, loads missing files on-the-fly.
17 | Ben Abelshausen
18 | https://github.com/itinero/srtm/blob/master/LICENSE.md
19 | https://github.com/itinero/srtm
20 | https://github.com/itinero/srtm/raw/master/icon.png
21 | https://github.com/itinero/srtm
22 | Git
23 | srtm, gis
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/SRTM/EmptySRTMDataCell.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace SRTM
5 | {
6 | public class EmptySRTMDataCell : ISRTMDataCell
7 | {
8 | public EmptySRTMDataCell(string filepath)
9 | {
10 | if (!File.Exists(filepath))
11 | throw new FileNotFoundException("File not found.", filepath);
12 |
13 | var filename = Path.GetFileName(filepath);
14 | filename = filename.Substring(0, filename.IndexOf('.')).ToLower(); // Path.GetFileNameWithoutExtension(filepath).ToLower();
15 | var fileCoordinate = filename.Split(new[] { 'e', 'w' });
16 | if (fileCoordinate.Length != 2)
17 | throw new ArgumentException("Invalid filename.", filepath);
18 |
19 | fileCoordinate[0] = fileCoordinate[0].TrimStart(new[] { 'n', 's' });
20 |
21 | Latitude = int.Parse(fileCoordinate[0]);
22 | if (filename.Contains("s"))
23 | Latitude *= -1;
24 |
25 | Longitude = int.Parse(fileCoordinate[1]);
26 | if (filename.Contains("w"))
27 | Longitude *= -1;
28 | }
29 |
30 | public int Latitude { get; private set; }
31 |
32 | public int Longitude { get; private set; }
33 |
34 | public int? GetElevation(double latitude, double longitude)
35 | {
36 | return null;
37 | }
38 |
39 | public double? GetElevationBilinear(double latitude, double longitude)
40 | {
41 | return null;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/SRTM/ISRTMSource.cs:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2019, Tadas Juščius
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | namespace SRTM
24 | {
25 | ///
26 | /// SRTM source interface.
27 | ///
28 | public interface ISRTMSource
29 | {
30 | ///
31 | /// Gets SRTM data cell from source.
32 | ///
33 | /// Local system path where to save the data to.
34 | /// Name of the file to get.
35 | ///
36 | bool GetMissingCell(string path, string name);
37 | }
38 | }
--------------------------------------------------------------------------------
/src/SRTM/Sources/SourceHelpers.cs:
--------------------------------------------------------------------------------
1 | using SRTM.Logging;
2 | using System;
3 | using System.IO;
4 | using System.Net;
5 | using System.Net.Http;
6 |
7 | namespace SRTM.Sources
8 | {
9 | public class SourceHelpers
10 | {
11 | ///
12 | /// Downloads a remote file and stores the data in the local one.
13 | ///
14 | public static bool Download(string local, string remote, bool logErrors = false)
15 | {
16 | var client = new HttpClient();
17 | return PerformDownload(client, local, remote, logErrors);
18 | }
19 |
20 | ///
21 | /// Downloads a remote file and stores the data in the local one. The given credentials are used for authorization.
22 | ///
23 | public static bool DownloadWithCredentials(NetworkCredential credentials, string local, string remote,
24 | bool logErrors = false)
25 | {
26 | HttpClientHandler handler = new HttpClientHandler {Credentials = credentials};
27 | var client = new HttpClient(handler);
28 | return PerformDownload(client, local, remote, logErrors);
29 | }
30 |
31 | private static bool PerformDownload(HttpClient client, string local, string remote, bool logErrors = false)
32 | {
33 | var Logger = LogProvider.For();
34 |
35 | try
36 | {
37 | if (File.Exists(local))
38 | {
39 | File.Delete(local);
40 | }
41 |
42 | using (var stream = client.GetStreamAsync(remote).Result)
43 | using (var outputStream = File.OpenWrite(local))
44 | {
45 | stream.CopyTo(outputStream);
46 | }
47 | return true;
48 | }
49 | catch (Exception ex)
50 | {
51 | if (logErrors)
52 | {
53 | Logger.ErrorException("Download failed.", ex);
54 | }
55 | }
56 | return false;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/SRTM/Sources/NASA/NASASource.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using SRTM.Logging;
3 |
4 | // The MIT License (MIT)
5 |
6 | // Copyright (c) 2019, Tadas Juščius
7 |
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 |
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 |
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | namespace SRTM.Sources.NASA
27 | {
28 | ///
29 | /// Defines a NASA source of data.
30 | ///
31 | public class NASASource : ISRTMSource
32 | {
33 | private NetworkCredential _credentials;
34 |
35 | public NASASource(NetworkCredential credentials)
36 | {
37 | _credentials = credentials;
38 | }
39 |
40 | ///
41 | /// The source of the data.
42 | ///
43 | public const string SOURCE = @"https://e4ftl01.cr.usgs.gov/MEASURES/SRTMGL1.003/2000.02.11/";
44 |
45 | ///
46 | /// Gets the missing cell.
47 | ///
48 | public bool GetMissingCell(string path, string name)
49 | {
50 | var filename = name + ".SRTMGL1.hgt.zip";
51 | var local = System.IO.Path.Combine(path, name + ".hgt.zip");
52 |
53 | var Logger = LogProvider.For();
54 | Logger.Info("Downloading {0} ...", name);
55 |
56 | return SourceHelpers.DownloadWithCredentials(_credentials, local, SOURCE + filename);
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/src/SRTM/ISRTMData.cs:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2017 Alpine Chough Software, Ben Abelshausen
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | namespace SRTM
24 | {
25 | ///
26 | /// SRTM data interface.
27 | ///
28 | public interface ISRTMData
29 | {
30 | ///
31 | /// Unloads all SRTM data cells.
32 | ///
33 | void Unload();
34 |
35 | ///
36 | /// Gets the elevation.
37 | ///
38 | ///
39 | /// The height. Null, if elevation is not available.
40 | ///
41 | ///
42 | ///
43 | ///
44 | /// Represents errors that occur during application execution.
45 | ///
46 | int? GetElevation(double latitude, double longitude);
47 |
48 | ///
49 | /// Gets the elevation. Data is smoothed using bilinear interpolation.
50 | ///
51 | ///
52 | /// The height. Null, if elevation is not available.
53 | ///
54 | ///
55 | ///
56 | ///
57 | /// Represents errors that occur during application execution.
58 | ///
59 | double? GetElevationBilinear(double latitude, double longitude);
60 | }
61 | }
--------------------------------------------------------------------------------
/src/SRTM/Sources/USGS/USGSSource.cs:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2017 Alpine Chough Software, Ben Abelshausen
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | using SRTM.Logging;
24 |
25 | namespace SRTM.Sources.USGS
26 | {
27 | ///
28 | /// Defines an USGS source of data.
29 | ///
30 | public class USGSSource : ISRTMSource
31 | {
32 | ///
33 | /// The source of the data.
34 | ///
35 | public const string SOURCE = @"https://dds.cr.usgs.gov/srtm/version2_1/SRTM3/";
36 |
37 | ///
38 | /// The continents to try.
39 | ///
40 | public static string[] CONTINENTS = new string[]
41 | {
42 | "Africa",
43 | "Australia",
44 | "Eurasia",
45 | "Islands",
46 | "North_America",
47 | "South_America"
48 | };
49 |
50 | ///
51 | /// Gets the missing cell.
52 | ///
53 | public bool GetMissingCell(string path, string name)
54 | {
55 | var filename = name + ".hgt.zip";
56 | var local = System.IO.Path.Combine(path, filename);
57 |
58 | var Logger = LogProvider.For();
59 | Logger.Info("Downloading {0} ...", name);
60 | foreach (var continent in CONTINENTS)
61 | {
62 | if (SourceHelpers.Download(local, SOURCE + continent + "/" + filename))
63 | {
64 | return true;
65 | }
66 | }
67 | return false;
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/SRTM.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27004.2009
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4B330D21-2EE5-4C34-B716-0DB1FBC58E29}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C142786B-F60E-4B6B-B9F2-1D25C3A3D7B5}"
9 | ProjectSection(SolutionItems) = preProject
10 | .gitignore = .gitignore
11 | README.md = README.md
12 | EndProjectSection
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{7E594B3F-4CA9-4198-B90D-DD16972D0481}"
15 | EndProject
16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{71C3E43F-7206-435F-8A43-92F79B2777B8}"
17 | EndProject
18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "releasenotes", "releasenotes", "{7B0DAA6C-67CD-4BCD-97AC-B81804EAA2F8}"
19 | EndProject
20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SRTM", "src\SRTM\SRTM.csproj", "{EB3BE394-DF15-49B8-8BC7-DA56DD0A0D67}"
21 | EndProject
22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SRTM.Tests.Functional", "test\SRTM.Tests.Functional\SRTM.Tests.Functional.csproj", "{0CC09A5A-321C-4A77-B61F-4E3B14A0BACB}"
23 | EndProject
24 | Global
25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
26 | Debug|Any CPU = Debug|Any CPU
27 | Release|Any CPU = Release|Any CPU
28 | EndGlobalSection
29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
30 | {EB3BE394-DF15-49B8-8BC7-DA56DD0A0D67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {EB3BE394-DF15-49B8-8BC7-DA56DD0A0D67}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {EB3BE394-DF15-49B8-8BC7-DA56DD0A0D67}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {EB3BE394-DF15-49B8-8BC7-DA56DD0A0D67}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {0CC09A5A-321C-4A77-B61F-4E3B14A0BACB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {0CC09A5A-321C-4A77-B61F-4E3B14A0BACB}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {0CC09A5A-321C-4A77-B61F-4E3B14A0BACB}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {0CC09A5A-321C-4A77-B61F-4E3B14A0BACB}.Release|Any CPU.Build.0 = Release|Any CPU
38 | EndGlobalSection
39 | GlobalSection(SolutionProperties) = preSolution
40 | HideSolutionNode = FALSE
41 | EndGlobalSection
42 | GlobalSection(NestedProjects) = preSolution
43 | {7B0DAA6C-67CD-4BCD-97AC-B81804EAA2F8} = {71C3E43F-7206-435F-8A43-92F79B2777B8}
44 | {EB3BE394-DF15-49B8-8BC7-DA56DD0A0D67} = {4B330D21-2EE5-4C34-B716-0DB1FBC58E29}
45 | {0CC09A5A-321C-4A77-B61F-4E3B14A0BACB} = {7E594B3F-4CA9-4198-B90D-DD16972D0481}
46 | EndGlobalSection
47 | GlobalSection(ExtensibilityGlobals) = postSolution
48 | SolutionGuid = {5714010C-59D1-4FFB-A854-0A4511E88222}
49 | EndGlobalSection
50 | EndGlobal
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SRTM
2 |
3 | [](https://github.com/itinero/srtm/actions/workflows/build.yml)
4 | [](https://gitter.im/Itinero/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
5 | [ ](http://www.itinero.tech/)
6 | [](https://github.com/itinero/srtm/blob/master/LICENSE.md)
7 |
8 | - SRTM: [](https://www.nuget.org/packages/SRTM/)
9 |
10 | A simple library to load SRTM data and return heights in _meter_ for a given lat/lon. Based on [Alpinechough.Srtm
11 | ](https://github.com/alpinechough/Alpinechough.Srtm).
12 |
13 | ## Usage
14 |
15 | ```csharp
16 | // create a new srtm data instance.
17 | // it accepts a folder to download and cache data into in addition to the source you want to use for the data.
18 | // USGS data is immediately available, but is of a lower resolution.
19 | var srtmData = new SRTMData(@"/path/to/data/cache", new USGSSource());
20 | // NASA data is of a higher resolution, but requires creating an account at https://urs.earthdata.nasa.gov/users/new/.
21 | var credentials = new NetworkCredential("username", "password");
22 | var srtmData = new SRTMData(@"/path/to/data/cache", new NASASource(credentials));
23 |
24 | // get elevations for some locations
25 | int? elevation = srtmData.GetElevation(47.267222, 11.392778);
26 | Console.WriteLine("Elevation of Innsbruck: {0}m", elevation);
27 |
28 | elevation = srtmData.GetElevation(-16.5, -68.15);
29 | Console.WriteLine("Elevation of La Paz: {0}m", elevation);
30 |
31 | elevation = srtmData.GetElevation(27.702983735525862f, 85.2978515625f);
32 | Console.WriteLine("Elevation of Kathmandu {0}m", elevation);
33 |
34 | elevation = srtmData.GetElevation(21.030673628606102f, 105.853271484375f);
35 | Console.WriteLine("Elevation of Ha Noi {0}m", elevation);
36 |
37 | // if a smoother result is preferred, it is possible to use bilinear interpolation at the cost of some accuracy
38 | double? smoothElevation = srtmData.GetElevationBilinear(47.267222, 11.392778);
39 | Console.WriteLine("Elevation of Innsbruck: {0}m", elevation);
40 |
41 | smoothElevation = srtmData.GetElevationBilinear(-16.5, -68.15);
42 | Console.WriteLine("Elevation of La Paz: {0}m", elevation);
43 |
44 | smoothElevation = srtmData.GetElevationBilinear(27.702983735525862f, 85.2978515625f);
45 | Console.WriteLine("Elevation of Kathmandu {0}m", elevation);
46 |
47 | smoothElevation = srtmData.GetElevationBilinear(21.030673628606102f, 105.853271484375f);
48 | Console.WriteLine("Elevation of Ha Noi {0}m", elevation);
49 | ```
50 |
51 | ## Data sources
52 |
53 | We implemented two sources of data, the [USGS SRTM](https://dds.cr.usgs.gov/srtm/version2_1/SRTM3/) and [NASA SRTM](https://e4ftl01.cr.usgs.gov/MEASURES/SRTMGL1.003/). If you want to add an extra source, we're accepting pull requests, you just need to implement [something like this](https://github.com/itinero/srtm/blob/master/src/SRTM/Sources/USGS/USGSSource.cs).
54 |
55 | ## We need help!
56 |
57 | If you think we need to add another source of data **let us know via the issues**, if you know more about SRTM or of another source of elevation, also **let us know**.
58 |
59 |
--------------------------------------------------------------------------------
/test/SRTM.Tests.Functional/Program.cs:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2017 Alpine Chough Software, Ben Abelshausen
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | using Serilog;
24 | using System;
25 | using SRTM.Sources.USGS;
26 |
27 | namespace SRTM.Tests.Functional
28 | {
29 | class Program
30 | {
31 | static void Main(string[] args)
32 | {
33 | var log = new LoggerConfiguration()
34 | .WriteTo.ColoredConsole(outputTemplate: "{Timestamp:HH:mm} [{Level}] ({Name:l}) {Message}{NewLine}{Exception}")
35 | .CreateLogger();
36 | Log.Logger = log;
37 |
38 | USGSTest();
39 | BilinearInterpolationTest();
40 |
41 | Console.WriteLine("Testing finished.");
42 | Console.ReadLine();
43 | }
44 |
45 | static void USGSTest()
46 | {
47 | Console.WriteLine("Start USGSTest.");
48 |
49 | // https://dds.cr.usgs.gov/srtm/version2_1/SRTM3/
50 | var srtmData = new SRTMData(@"srtm-cache", new USGSSource());
51 |
52 | int? elevationInnsbruck = srtmData.GetElevation(47.267222, 11.392778);
53 | Console.WriteLine("Elevation of Innsbruck: {0}m", elevationInnsbruck);
54 |
55 | int? elevationLaPaz = srtmData.GetElevation(-16.5, -68.15);
56 | Console.WriteLine("Elevation of La Paz: {0}m", elevationLaPaz);
57 |
58 | int? elevationKathmandu = srtmData.GetElevation(27.702983735525862f, 85.2978515625f);
59 | Console.WriteLine("Elevation of Kathmandu {0}m", elevationKathmandu);
60 |
61 | int? elevationHanoi = srtmData.GetElevation(21.030673628606102f, 105.853271484375f);
62 | Console.WriteLine("Elevation of Ha Noi {0}m", elevationHanoi);
63 |
64 | // tries to get elevation from an empty cell.
65 | int? elevationSomeplace1 = srtmData.GetElevation(52.02237f, 2.55853224f);
66 | Console.WriteLine("Elevation of nowhere returns {0}", elevationSomeplace1);
67 |
68 | int? elevationNamibia1 = srtmData.GetElevation(-20, 19.89597);
69 | Console.WriteLine("Elevation of namibia1 returns {0}", elevationNamibia1);
70 |
71 | Console.WriteLine("End USGSTest.");
72 | }
73 |
74 | static void BilinearInterpolationTest()
75 | {
76 | Console.WriteLine("Start BilinearInterpolationTest.");
77 |
78 | // https://dds.cr.usgs.gov/srtm/version2_1/SRTM3/
79 | var srtmData = new SRTMData(@"srtm-cache", new USGSSource());
80 |
81 | double? elevationInnsbruck = srtmData.GetElevationBilinear(47.267222, 11.392778);
82 | Console.WriteLine("Bilinear elevation of Innsbruck: {0}m", elevationInnsbruck);
83 |
84 | double? elevationLaPaz = srtmData.GetElevationBilinear(-16.5, -68.15);
85 | Console.WriteLine("Elevation of La Paz: {0}m", elevationLaPaz);
86 |
87 | double? elevationKathmandu = srtmData.GetElevationBilinear(27.702983735525862f, 85.2978515625f);
88 | Console.WriteLine("Elevation of Kathmandu {0}m", elevationKathmandu);
89 |
90 | double? elevationHanoi = srtmData.GetElevationBilinear(21.030673628606102f, 105.853271484375f);
91 | Console.WriteLine("Elevation of Ha Noi {0}m", elevationHanoi);
92 |
93 | // tries to get elevation from an empty cell.
94 | double? elevationSomeplace1 = srtmData.GetElevationBilinear(52.02237f, 2.55853224f);
95 | Console.WriteLine("Elevation of nowhere returns {0}", elevationSomeplace1);
96 |
97 | double? elevationNamibia1 = srtmData.GetElevationBilinear(-20, 19.89597);
98 | Console.WriteLine("Elevation of namibia1 returns {0}", elevationNamibia1);
99 |
100 | //Testing interpolation across cell edges
101 | double? elevationOxted = srtmData.GetElevationBilinear(51.2525, 0.00001);
102 | Console.WriteLine("Elevation of Oxted {0}m", elevationOxted);
103 |
104 | Console.WriteLine("End BilinearInterpolationTest.");
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/SRTM/SRTMDataCell.cs:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2017 Alpine Chough Software, Ben Abelshausen
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | using System;
24 | using System.IO;
25 |
26 | namespace SRTM
27 | {
28 | ///
29 | /// SRTM data cell.
30 | ///
31 | ///
32 | /// Is thrown when a file path argument specifies a file that does not exist.
33 | ///
34 | ///
35 | /// Is thrown when an argument passed to a method is invalid.
36 | ///
37 | ///
38 | /// Is thrown when an argument passed to a method is invalid because it is outside the allowable range of values as
39 | /// specified by the method.
40 | ///
41 | public class SRTMDataCell : ISRTMDataCell
42 | {
43 | #region Lifecycle
44 |
45 | ///
46 | /// Initializes a new instance of the class.
47 | ///
48 | ///
49 | /// Filepath.
50 | ///
51 | ///
52 | /// Is thrown when a file path argument specifies a file that does not exist.
53 | ///
54 | ///
55 | /// Is thrown when an argument passed to a method is invalid.
56 | ///
57 | public SRTMDataCell(string filepath)
58 | {
59 | if (!File.Exists(filepath))
60 | throw new FileNotFoundException("File not found.", filepath);
61 |
62 | var filename = Path.GetFileName(filepath);
63 | filename = filename.Substring(0, filename.IndexOf('.')).ToLower(); // Path.GetFileNameWithoutExtension(filepath).ToLower();
64 | var fileCoordinate = filename.Split(new[] { 'e', 'w' });
65 | if (fileCoordinate.Length != 2)
66 | throw new ArgumentException("Invalid filename.", filepath);
67 |
68 | fileCoordinate[0] = fileCoordinate[0].TrimStart(new[] { 'n', 's' });
69 |
70 | Latitude = int.Parse(fileCoordinate[0]);
71 | if (filename.Contains("s"))
72 | Latitude *= -1;
73 |
74 | Longitude = int.Parse(fileCoordinate[1]);
75 | if (filename.Contains("w"))
76 | Longitude *= -1;
77 |
78 | if (filepath.EndsWith(".zip"))
79 | {
80 | using (var stream = File.OpenRead(filepath))
81 | using (var archive = new System.IO.Compression.ZipArchive(stream))
82 | using (var memoryStream = new MemoryStream())
83 | {
84 | using (var hgt = archive.Entries[0].Open())
85 | {
86 | hgt.CopyTo(memoryStream);
87 | HgtData = memoryStream.ToArray();
88 | }
89 | }
90 | }
91 | else
92 | {
93 | HgtData = File.ReadAllBytes(filepath);
94 | }
95 |
96 | switch (HgtData.Length)
97 | {
98 | case 1201 * 1201 * 2: // SRTM-3
99 | PointsPerCell = 1201;
100 | break;
101 | case 3601 * 3601 * 2: // SRTM-1
102 | PointsPerCell = 3601;
103 | break;
104 | default:
105 | throw new ArgumentException("Invalid file size.", filepath);
106 | }
107 | }
108 |
109 | #endregion
110 |
111 | #region Properties
112 |
113 | ///
114 | /// Gets or sets the hgt data.
115 | ///
116 | ///
117 | /// The hgt data.
118 | ///
119 | private byte[] HgtData { get; set; }
120 |
121 | ///
122 | /// Gets or sets the points per cell.
123 | ///
124 | ///
125 | /// The points per cell.
126 | ///
127 | private int PointsPerCell { get; set; }
128 |
129 | ///
130 | /// Gets or sets the latitude of the srtm data file.
131 | ///
132 | ///
133 | /// The latitude.
134 | ///
135 | public int Latitude { get; private set; }
136 |
137 | ///
138 | /// Gets or sets the longitude of the srtm data file.
139 | ///
140 | ///
141 | /// The longitude.
142 | ///
143 | public int Longitude { get; private set; }
144 |
145 | #endregion
146 |
147 | #region Public Methods
148 |
149 | ///
150 | /// Gets the elevation.
151 | ///
152 | ///
153 | /// The height. Null, if elevation is not available.
154 | ///
155 | ///
156 | ///
157 | ///
158 | /// Represents errors that occur during application execution.
159 | ///
160 | public int? GetElevation(double latitude, double longitude)
161 | {
162 | int localLat = (int)((latitude - Latitude) * PointsPerCell);
163 | int localLon = (int)(((longitude - Longitude)) * PointsPerCell);
164 | return ReadByteData(localLat, localLon);
165 | }
166 |
167 | ///
168 | /// Gets the elevation. Data is smoothed using bilinear interpolation.
169 | ///
170 | ///
171 | /// The height. Null, if elevation is not available.
172 | ///
173 | ///
174 | ///
175 | ///
176 | /// Represents errors that occur during application execution.
177 | ///
178 | public double? GetElevationBilinear(double latitude, double longitude)
179 | {
180 | double localLat = (latitude - Latitude) * PointsPerCell;
181 | double localLon = (longitude - Longitude) * PointsPerCell;
182 |
183 | int localLatMin = (int) Math.Floor(localLat);
184 | int localLonMin = (int) Math.Floor(localLon);
185 | int localLatMax = (int) Math.Ceiling(localLat);
186 | int localLonMax = (int) Math.Ceiling(localLon);
187 |
188 | int? elevation00 = ReadByteData(localLatMin, localLonMin);
189 | int? elevation10 = ReadByteData(localLatMax, localLonMin);
190 | int? elevation01 = ReadByteData(localLatMin, localLonMax);
191 | int? elevation11 = ReadByteData(localLatMax, localLonMax);
192 |
193 | if (!elevation00.HasValue || !elevation10.HasValue || !elevation01.HasValue || !elevation11.HasValue)
194 | {
195 | //Can't do bilinear if missing one of the points. Default to regular.
196 | return (double)GetElevation(latitude, longitude);
197 | }
198 |
199 | double deltaLat = localLatMax - localLat;
200 | double deltaLon = localLonMax - localLon;
201 |
202 | return Blerp((double) elevation00, (double) elevation10, (double) elevation01, (double) elevation11,
203 | deltaLat, deltaLon);
204 | }
205 |
206 | #endregion
207 |
208 | #region Private Methods
209 |
210 | ///
211 | /// Method responsible for reading byte data from data cell file.
212 | ///
213 | /// Local latitude within the data cell
214 | /// Local longitude within the data cell
215 | /// Height read from data cell file
216 | ///
217 | private int? ReadByteData(int localLat, int localLon)
218 | {
219 | int bytesPos = ((PointsPerCell - localLat - 1) * PointsPerCell * 2) + localLon * 2;
220 |
221 | if (bytesPos < 0 || bytesPos > PointsPerCell * PointsPerCell * 2)
222 | throw new ArgumentOutOfRangeException("Coordinates out of range.", "coordinates");
223 |
224 | if (bytesPos >= HgtData.Length)
225 | return null;
226 |
227 | if ((HgtData[bytesPos] == 0x80) && (HgtData[bytesPos + 1] == 0x00))
228 | return null;
229 |
230 | // Motorola "big-endian" order with the most significant byte first
231 | return (HgtData[bytesPos]) << 8 | HgtData[bytesPos + 1];
232 | }
233 |
234 | private double Lerp(double start, double end, double delta)
235 | {
236 | return start + (end - start) * delta;
237 | }
238 |
239 | private double Blerp(double val00, double val10, double val01, double val11, double deltaX, double deltaY)
240 | {
241 | return Lerp(Lerp(val11, val01, deltaX), Lerp(val10, val00, deltaX), deltaY);
242 | }
243 |
244 | #endregion
245 | }
246 | }
--------------------------------------------------------------------------------
/src/SRTM/SRTMData.cs:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2017 Alpine Chough Software, Ben Abelshausen
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | using System;
24 | using System.Collections.Generic;
25 | using System.IO;
26 | using System.Linq;
27 | using SRTM.Sources;
28 |
29 | namespace SRTM
30 | {
31 | ///
32 | /// SRTM Data.
33 | ///
34 | ///
35 | /// Is thrown when part of a file or directory argument cannot be found.
36 | ///
37 | public class SRTMData : ISRTMData
38 | {
39 | private const int RETRIES = 3;
40 | private ISRTMSource _source;
41 |
42 | ///
43 | /// Initializes a new instance of the class.
44 | ///
45 | ///
46 | /// Data directory.
47 | ///
48 | ///
49 | /// Data source to use. Must be an instance of the class
50 | ///
51 | ///
52 | /// Is thrown when part of a file or directory argument cannot be found.
53 | ///
54 | public SRTMData(string dataDirectory, ISRTMSource source)
55 | {
56 | if (!Directory.Exists(dataDirectory))
57 | throw new DirectoryNotFoundException(dataDirectory);
58 |
59 | _source = source;
60 | GetMissingCell = _source.GetMissingCell;
61 | DataDirectory = dataDirectory;
62 | DataCells = new List();
63 | }
64 |
65 | ///
66 | /// Initializes a new instance of the class.
67 | ///
68 | ///
69 | /// Data directory.
70 | ///
71 | ///
72 | /// Gets a missing cell and writes it to the given path.
73 | ///
74 | ///
75 | /// Is thrown when part of a file or directory argument cannot be found.
76 | ///
77 | public SRTMData(string dataDirectory, Func<(string path, string name), bool> getMissingCell)
78 | : this(dataDirectory, new FuncSource(getMissingCell))
79 | {
80 |
81 | }
82 |
83 | ///
84 | /// A delegate to get missing cells.
85 | ///
86 | public delegate bool GetMissingCellDelegate(string path, string name);
87 |
88 | ///
89 | /// Gets or sets the missing cell delegate.
90 | ///
91 | public GetMissingCellDelegate GetMissingCell { get; set; }
92 |
93 | ///
94 | /// Gets or sets the data directory.
95 | ///
96 | ///
97 | /// The data directory.
98 | ///
99 | public string DataDirectory { get; private set; }
100 |
101 | ///
102 | /// Gets or sets the SRTM data cells.
103 | ///
104 | ///
105 | /// The SRTM data cells.
106 | ///
107 | private List DataCells { get; set; }
108 |
109 | #region Public methods
110 |
111 | ///
112 | /// Unloads all SRTM data cells.
113 | ///
114 | public void Unload()
115 | {
116 | DataCells.Clear();
117 | }
118 |
119 | ///
120 | /// Gets the elevation.
121 | ///
122 | ///
123 | /// The height. Null, if elevation is not available.
124 | ///
125 | ///
126 | /// Latitude in decimal degrees of desired location.
127 | ///
128 | ///
129 | /// Longitude in decimal degrees of desired location
130 | ///
131 | ///
132 | /// Represents errors that occur during application execution.
133 | ///
134 | public int? GetElevation(double latitude, double longitude)
135 | {
136 | ISRTMDataCell dataCell = GetDataCell(latitude, longitude);
137 | return dataCell.GetElevation(latitude, longitude);
138 | }
139 |
140 | ///
141 | /// Gets the elevation. Data is smoothed using bilinear interpolation.
142 | ///
143 | ///
144 | /// The height. Null, if elevation is not available.
145 | ///
146 | ///
147 | /// Latitude in decimal degrees of desired location.
148 | ///
149 | ///
150 | /// Longitude in decimal degrees of desired location
151 | ///
152 | ///
153 | /// Represents errors that occur during application execution.
154 | ///
155 | public double? GetElevationBilinear(double latitude, double longitude)
156 | {
157 | ISRTMDataCell dataCell = GetDataCell(latitude, longitude);
158 | return dataCell.GetElevationBilinear(latitude, longitude);
159 | }
160 |
161 | #endregion
162 |
163 | #region Private methods
164 |
165 | ///
166 | /// Method responsible for identifying the correct data cell and either retrieving it from cache ir downloading it from source.
167 | ///
168 | ///
169 | ///
170 | /// Elevation data cell. Must be an instance of the class.
171 | private ISRTMDataCell GetDataCell(double latitude, double longitude)
172 | {
173 | int cellLatitude = (int)Math.Floor(Math.Abs(latitude));
174 | if (latitude < 0)
175 | {
176 | cellLatitude *= -1;
177 | if (cellLatitude != latitude)
178 | { // if exactly equal, keep the current tile.
179 | cellLatitude -= 1; // because negative so in bottom tile
180 | }
181 | }
182 |
183 | int cellLongitude = (int)Math.Floor(Math.Abs(longitude));
184 | if (longitude < 0)
185 | {
186 | cellLongitude *= -1;
187 | if (cellLongitude != longitude)
188 | { // if exactly equal, keep the current tile.
189 | cellLongitude -= 1; // because negative so in left tile
190 | }
191 | }
192 |
193 | var dataCell = DataCells.Where(dc => dc.Latitude == cellLatitude && dc.Longitude == cellLongitude).FirstOrDefault();
194 | if (dataCell != null)
195 | {
196 | return dataCell;
197 | }
198 |
199 | string filename = string.Format("{0}{1:D2}{2}{3:D3}",
200 | cellLatitude < 0 ? "S" : "N",
201 | Math.Abs(cellLatitude),
202 | cellLongitude < 0 ? "W" : "E",
203 | Math.Abs(cellLongitude));
204 |
205 | var filePath = Path.Combine(DataDirectory, filename + ".hgt");
206 | var zipFilePath = Path.Combine(DataDirectory, filename + ".hgt.zip");
207 | var txtFilePath = Path.Combine(DataDirectory, filename + ".txt");
208 | var count = -1;
209 |
210 | if (!File.Exists(filePath) && !File.Exists(zipFilePath) && !File.Exists(txtFilePath) &&
211 | this.GetMissingCell != null)
212 | {
213 | this.GetMissingCell(DataDirectory, filename);
214 | }
215 | else if(File.Exists(txtFilePath) && this.GetMissingCell != null)
216 | {
217 | var txtFile = File.ReadAllText(txtFilePath);
218 | if (!int.TryParse(txtFile, out count))
219 | {
220 | File.Delete(txtFilePath);
221 | count = -1;
222 | }
223 | else if(count < RETRIES)
224 | {
225 | if (this.GetMissingCell(DataDirectory, filename))
226 | {
227 | File.Delete(txtFilePath);
228 | }
229 | }
230 | }
231 |
232 | if (File.Exists(filePath))
233 | {
234 | dataCell = new SRTMDataCell(filePath);
235 | }
236 | else if(File.Exists(zipFilePath))
237 | {
238 | dataCell = new SRTMDataCell(zipFilePath);
239 | }
240 | else
241 | {
242 | if (count < 0)
243 | {
244 | File.WriteAllText(txtFilePath, "1");
245 | return GetDataCell(latitude, longitude);
246 | }
247 | else if (count < RETRIES)
248 | {
249 | count++;
250 | File.WriteAllText(txtFilePath, count.ToString());
251 | return GetDataCell(latitude, longitude);
252 | }
253 | else
254 | {
255 | dataCell = new EmptySRTMDataCell(txtFilePath);
256 | }
257 | }
258 | DataCells.Add(dataCell);
259 |
260 | return dataCell;
261 | }
262 |
263 | #endregion
264 | }
265 | }
--------------------------------------------------------------------------------
/src/SRTM/Logging/LibLog.cs:
--------------------------------------------------------------------------------
1 | //
2 | //===============================================================================
3 | // LibLog
4 | //
5 | // https://github.com/damianh/LibLog
6 | //===============================================================================
7 | // Copyright © 2011-2017 Damian Hickey. All rights reserved.
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | // SOFTWARE.
26 | //===============================================================================
27 |
28 | // ReSharper disable PossibleNullReferenceException
29 |
30 | // Define LIBLOG_PORTABLE conditional compilation symbol for PCL compatibility
31 | //
32 | // Define LIBLOG_PUBLIC to enable ability to GET a logger (LogProvider.For<>() etc) from outside this library. NOTE:
33 | // this can have unintended consequences of consumers of your library using your library to resolve a logger. If the
34 | // reason is because you want to open this functionality to other projects within your solution,
35 | // consider [InternalsVisibleTo] instead.
36 | //
37 | // Define LIBLOG_PROVIDERS_ONLY if your library provides its own logging API and you just want to use the
38 | // LibLog providers internally to provide built in support for popular logging frameworks.
39 |
40 | #pragma warning disable 1591
41 |
42 | using System.Diagnostics.CodeAnalysis;
43 |
44 | [assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "YourRootNamespace.Logging")]
45 | [assembly: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "YourRootNamespace.Logging.Logger.#Invoke(YourRootNamespace.Logging.LogLevel,System.Func`1,System.Exception,System.Object[])")]
46 |
47 | // If you copied this file manually, you need to change all "YourRootNameSpace" so not to clash with other libraries
48 | // that use LibLog
49 | #if LIBLOG_PROVIDERS_ONLY
50 | namespace YourRootNamespace.LibLog
51 | #else
52 | namespace SRTM.Logging
53 | #endif
54 | {
55 | using System.Collections.Generic;
56 | using System.Diagnostics.CodeAnalysis;
57 | #if LIBLOG_PROVIDERS_ONLY
58 | using SRTM.LibLog.LogProviders;
59 | #else
60 | using SRTM.Logging.LogProviders;
61 | #endif
62 | using System;
63 | #if !LIBLOG_PROVIDERS_ONLY
64 | using System.Diagnostics;
65 | #if !LIBLOG_PORTABLE
66 | using System.Runtime.CompilerServices;
67 | #endif
68 | #endif
69 |
70 | #if LIBLOG_PROVIDERS_ONLY
71 | internal
72 | #else
73 | public
74 | #endif
75 | delegate bool Logger(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters);
76 |
77 | #if !LIBLOG_PROVIDERS_ONLY
78 | ///
79 | /// Simple interface that represent a logger.
80 | ///
81 | #if LIBLOG_PUBLIC
82 | public
83 | #else
84 | internal
85 | #endif
86 | interface ILog
87 | {
88 | ///
89 | /// Log a message the specified log level.
90 | ///
91 | /// The log level.
92 | /// The message function.
93 | /// An optional exception.
94 | /// Optional format parameters for the message generated by the messagefunc.
95 | /// true if the message was logged. Otherwise false.
96 | ///
97 | /// Note to implementers: the message func should not be called if the loglevel is not enabled
98 | /// so as not to incur performance penalties.
99 | ///
100 | /// To check IsEnabled call Log with only LogLevel and check the return value, no event will be written.
101 | ///
102 | bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters);
103 | }
104 | #endif
105 |
106 | ///
107 | /// The log level.
108 | ///
109 | #if LIBLOG_PROVIDERS_ONLY
110 | internal
111 | #else
112 | public
113 | #endif
114 | enum LogLevel
115 | {
116 | Trace,
117 | Debug,
118 | Info,
119 | Warn,
120 | Error,
121 | Fatal
122 | }
123 |
124 | #if !LIBLOG_PROVIDERS_ONLY
125 | #if !LIBLOG_PORTABLE
126 | [ExcludeFromCodeCoverage]
127 | #endif
128 | #if LIBLOG_PUBLIC
129 | public
130 | #else
131 | internal
132 | #endif
133 | static partial class LogExtensions
134 | {
135 | public static bool IsDebugEnabled(this ILog logger)
136 | {
137 | GuardAgainstNullLogger(logger);
138 | return logger.Log(LogLevel.Debug, null);
139 | }
140 |
141 | public static bool IsErrorEnabled(this ILog logger)
142 | {
143 | GuardAgainstNullLogger(logger);
144 | return logger.Log(LogLevel.Error, null);
145 | }
146 |
147 | public static bool IsFatalEnabled(this ILog logger)
148 | {
149 | GuardAgainstNullLogger(logger);
150 | return logger.Log(LogLevel.Fatal, null);
151 | }
152 |
153 | public static bool IsInfoEnabled(this ILog logger)
154 | {
155 | GuardAgainstNullLogger(logger);
156 | return logger.Log(LogLevel.Info, null);
157 | }
158 |
159 | public static bool IsTraceEnabled(this ILog logger)
160 | {
161 | GuardAgainstNullLogger(logger);
162 | return logger.Log(LogLevel.Trace, null);
163 | }
164 |
165 | public static bool IsWarnEnabled(this ILog logger)
166 | {
167 | GuardAgainstNullLogger(logger);
168 | return logger.Log(LogLevel.Warn, null);
169 | }
170 |
171 | public static void Debug(this ILog logger, Func messageFunc)
172 | {
173 | GuardAgainstNullLogger(logger);
174 | logger.Log(LogLevel.Debug, WrapLogInternal(messageFunc));
175 | }
176 |
177 | public static void Debug(this ILog logger, string message)
178 | {
179 | if (logger.IsDebugEnabled())
180 | {
181 | logger.Log(LogLevel.Debug, message.AsFunc());
182 | }
183 | }
184 |
185 | public static void Debug(this ILog logger, string message, params object[] args)
186 | {
187 | logger.DebugFormat(message, args);
188 | }
189 |
190 | public static void Debug(this ILog logger, Exception exception, string message, params object[] args)
191 | {
192 | logger.DebugException(message, exception, args);
193 | }
194 |
195 | public static void DebugFormat(this ILog logger, string message, params object[] args)
196 | {
197 | if (logger.IsDebugEnabled())
198 | {
199 | logger.LogFormat(LogLevel.Debug, message, args);
200 | }
201 | }
202 |
203 | public static void DebugException(this ILog logger, string message, Exception exception)
204 | {
205 | if (logger.IsDebugEnabled())
206 | {
207 | logger.Log(LogLevel.Debug, message.AsFunc(), exception);
208 | }
209 | }
210 |
211 | public static void DebugException(this ILog logger, string message, Exception exception, params object[] formatParams)
212 | {
213 | if (logger.IsDebugEnabled())
214 | {
215 | logger.Log(LogLevel.Debug, message.AsFunc(), exception, formatParams);
216 | }
217 | }
218 |
219 | public static void Error(this ILog logger, Func messageFunc)
220 | {
221 | GuardAgainstNullLogger(logger);
222 | logger.Log(LogLevel.Error, WrapLogInternal(messageFunc));
223 | }
224 |
225 | public static void Error(this ILog logger, string message)
226 | {
227 | if (logger.IsErrorEnabled())
228 | {
229 | logger.Log(LogLevel.Error, message.AsFunc());
230 | }
231 | }
232 |
233 | public static void Error(this ILog logger, string message, params object[] args)
234 | {
235 | logger.ErrorFormat(message, args);
236 | }
237 |
238 | public static void Error(this ILog logger, Exception exception, string message, params object[] args)
239 | {
240 | logger.ErrorException(message, exception, args);
241 | }
242 |
243 | public static void ErrorFormat(this ILog logger, string message, params object[] args)
244 | {
245 | if (logger.IsErrorEnabled())
246 | {
247 | logger.LogFormat(LogLevel.Error, message, args);
248 | }
249 | }
250 |
251 | public static void ErrorException(this ILog logger, string message, Exception exception, params object[] formatParams)
252 | {
253 | if (logger.IsErrorEnabled())
254 | {
255 | logger.Log(LogLevel.Error, message.AsFunc(), exception, formatParams);
256 | }
257 | }
258 |
259 | public static void Fatal(this ILog logger, Func messageFunc)
260 | {
261 | logger.Log(LogLevel.Fatal, WrapLogInternal(messageFunc));
262 | }
263 |
264 | public static void Fatal(this ILog logger, string message)
265 | {
266 | if (logger.IsFatalEnabled())
267 | {
268 | logger.Log(LogLevel.Fatal, message.AsFunc());
269 | }
270 | }
271 |
272 | public static void Fatal(this ILog logger, string message, params object[] args)
273 | {
274 | logger.FatalFormat(message, args);
275 | }
276 |
277 | public static void Fatal(this ILog logger, Exception exception, string message, params object[] args)
278 | {
279 | logger.FatalException(message, exception, args);
280 | }
281 |
282 | public static void FatalFormat(this ILog logger, string message, params object[] args)
283 | {
284 | if (logger.IsFatalEnabled())
285 | {
286 | logger.LogFormat(LogLevel.Fatal, message, args);
287 | }
288 | }
289 |
290 | public static void FatalException(this ILog logger, string message, Exception exception, params object[] formatParams)
291 | {
292 | if (logger.IsFatalEnabled())
293 | {
294 | logger.Log(LogLevel.Fatal, message.AsFunc(), exception, formatParams);
295 | }
296 | }
297 |
298 | public static void Info(this ILog logger, Func messageFunc)
299 | {
300 | GuardAgainstNullLogger(logger);
301 | logger.Log(LogLevel.Info, WrapLogInternal(messageFunc));
302 | }
303 |
304 | public static void Info(this ILog logger, string message)
305 | {
306 | if (logger.IsInfoEnabled())
307 | {
308 | logger.Log(LogLevel.Info, message.AsFunc());
309 | }
310 | }
311 |
312 | public static void Info(this ILog logger, string message, params object[] args)
313 | {
314 | logger.InfoFormat(message, args);
315 | }
316 |
317 | public static void Info(this ILog logger, Exception exception, string message, params object[] args)
318 | {
319 | logger.InfoException(message, exception, args);
320 | }
321 |
322 | public static void InfoFormat(this ILog logger, string message, params object[] args)
323 | {
324 | if (logger.IsInfoEnabled())
325 | {
326 | logger.LogFormat(LogLevel.Info, message, args);
327 | }
328 | }
329 |
330 | public static void InfoException(this ILog logger, string message, Exception exception, params object[] formatParams)
331 | {
332 | if (logger.IsInfoEnabled())
333 | {
334 | logger.Log(LogLevel.Info, message.AsFunc(), exception, formatParams);
335 | }
336 | }
337 |
338 | public static void Trace(this ILog logger, Func messageFunc)
339 | {
340 | GuardAgainstNullLogger(logger);
341 | logger.Log(LogLevel.Trace, WrapLogInternal(messageFunc));
342 | }
343 |
344 | public static void Trace(this ILog logger, string message)
345 | {
346 | if (logger.IsTraceEnabled())
347 | {
348 | logger.Log(LogLevel.Trace, message.AsFunc());
349 | }
350 | }
351 |
352 | public static void Trace(this ILog logger, string message, params object[] args)
353 | {
354 | logger.TraceFormat(message, args);
355 | }
356 |
357 | public static void Trace(this ILog logger, Exception exception, string message, params object[] args)
358 | {
359 | logger.TraceException(message, exception, args);
360 | }
361 |
362 | public static void TraceFormat(this ILog logger, string message, params object[] args)
363 | {
364 | if (logger.IsTraceEnabled())
365 | {
366 | logger.LogFormat(LogLevel.Trace, message, args);
367 | }
368 | }
369 |
370 | public static void TraceException(this ILog logger, string message, Exception exception, params object[] formatParams)
371 | {
372 | if (logger.IsTraceEnabled())
373 | {
374 | logger.Log(LogLevel.Trace, message.AsFunc(), exception, formatParams);
375 | }
376 | }
377 |
378 | public static void Warn(this ILog logger, Func messageFunc)
379 | {
380 | GuardAgainstNullLogger(logger);
381 | logger.Log(LogLevel.Warn, WrapLogInternal(messageFunc));
382 | }
383 |
384 | public static void Warn(this ILog logger, string message)
385 | {
386 | if (logger.IsWarnEnabled())
387 | {
388 | logger.Log(LogLevel.Warn, message.AsFunc());
389 | }
390 | }
391 |
392 | public static void Warn(this ILog logger, string message, params object[] args)
393 | {
394 | logger.WarnFormat(message, args);
395 | }
396 |
397 | public static void Warn(this ILog logger, Exception exception, string message, params object[] args)
398 | {
399 | logger.WarnException(message, exception, args);
400 | }
401 |
402 | public static void WarnFormat(this ILog logger, string message, params object[] args)
403 | {
404 | if (logger.IsWarnEnabled())
405 | {
406 | logger.LogFormat(LogLevel.Warn, message, args);
407 | }
408 | }
409 |
410 | public static void WarnException(this ILog logger, string message, Exception exception, params object[] formatParams)
411 | {
412 | if (logger.IsWarnEnabled())
413 | {
414 | logger.Log(LogLevel.Warn, message.AsFunc(), exception, formatParams);
415 | }
416 | }
417 |
418 | // ReSharper disable once UnusedParameter.Local
419 | private static void GuardAgainstNullLogger(ILog logger)
420 | {
421 | if (logger == null)
422 | {
423 | throw new ArgumentNullException("logger");
424 | }
425 | }
426 |
427 | private static void LogFormat(this ILog logger, LogLevel logLevel, string message, params object[] args)
428 | {
429 | logger.Log(logLevel, message.AsFunc(), null, args);
430 | }
431 |
432 | // Avoid the closure allocation, see https://gist.github.com/AArnott/d285feef75c18f6ecd2b
433 | private static Func AsFunc(this T value) where T : class
434 | {
435 | return value.Return;
436 | }
437 |
438 | private static T Return(this T value)
439 | {
440 | return value;
441 | }
442 |
443 | // Allow passing callsite-logger-type to LogProviderBase using messageFunc
444 | internal static Func WrapLogSafeInternal(LoggerExecutionWrapper logger, Func messageFunc)
445 | {
446 | Func wrappedMessageFunc = () =>
447 | {
448 | try
449 | {
450 | return messageFunc();
451 | }
452 | catch (Exception ex)
453 | {
454 | logger.WrappedLogger(LogLevel.Error, () => LoggerExecutionWrapper.FailedToGenerateLogMessage, ex);
455 | }
456 | return null;
457 | };
458 | return wrappedMessageFunc;
459 | }
460 |
461 | // Allow passing callsite-logger-type to LogProviderBase using messageFunc
462 | private static Func WrapLogInternal(Func messageFunc)
463 | {
464 | Func wrappedMessageFunc = () =>
465 | {
466 | return messageFunc();
467 | };
468 | return wrappedMessageFunc;
469 | }
470 | }
471 | #endif
472 |
473 | ///
474 | /// Represents a way to get a
475 | ///
476 | #if LIBLOG_PROVIDERS_ONLY
477 | internal
478 | #else
479 | public
480 | #endif
481 | interface ILogProvider
482 | {
483 | ///
484 | /// Gets the specified named logger.
485 | ///
486 | /// Name of the logger.
487 | /// The logger reference.
488 | Logger GetLogger(string name);
489 |
490 | ///
491 | /// Opens a nested diagnostics context. Not supported in EntLib logging.
492 | ///
493 | /// The message to add to the diagnostics context.
494 | /// A disposable that when disposed removes the message from the context.
495 | IDisposable OpenNestedContext(string message);
496 |
497 | ///
498 | /// Opens a mapped diagnostics context. Not supported in EntLib logging.
499 | ///
500 | /// A key.
501 | /// A value.
502 | /// A disposable that when disposed removes the map from the context.
503 | IDisposable OpenMappedContext(string key, object value, bool destructure = false);
504 | }
505 |
506 | ///
507 | /// Provides a mechanism to create instances of objects.
508 | ///
509 | #if !LIBLOG_PORTABLE
510 | [ExcludeFromCodeCoverage]
511 | #endif
512 | #if LIBLOG_PROVIDERS_ONLY
513 | internal
514 | #else
515 | public
516 | #endif
517 | static class LogProvider
518 | {
519 | #if !LIBLOG_PROVIDERS_ONLY
520 | private const string NullLogProvider = "Current Log Provider is not set. Call SetCurrentLogProvider " +
521 | "with a non-null value first.";
522 | private static dynamic s_currentLogProvider;
523 | private static Action s_onCurrentLogProviderSet;
524 | private static Lazy s_resolvedLogProvider = new Lazy(() => ForceResolveLogProvider());
525 |
526 | [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")]
527 | static LogProvider()
528 | {
529 | IsDisabled = false;
530 | }
531 |
532 | ///
533 | /// Sets the current log provider.
534 | ///
535 | /// The log provider.
536 | public static void SetCurrentLogProvider(ILogProvider logProvider)
537 | {
538 | s_currentLogProvider = logProvider;
539 |
540 | RaiseOnCurrentLogProviderSet();
541 | }
542 |
543 | ///
544 | /// Gets or sets a value indicating whether this is logging is disabled.
545 | ///
546 | ///
547 | /// true if logging is disabled; otherwise, false.
548 | ///
549 | public static bool IsDisabled { get; set; }
550 |
551 | ///
552 | /// Sets an action that is invoked when a consumer of your library has called SetCurrentLogProvider. It is
553 | /// important that hook into this if you are using child libraries (especially ilmerged ones) that are using
554 | /// LibLog (or other logging abstraction) so you adapt and delegate to them.
555 | ///
556 | ///
557 | internal static Action OnCurrentLogProviderSet
558 | {
559 | set
560 | {
561 | s_onCurrentLogProviderSet = value;
562 | RaiseOnCurrentLogProviderSet();
563 | }
564 | }
565 |
566 | internal static ILogProvider CurrentLogProvider
567 | {
568 | get
569 | {
570 | return s_currentLogProvider;
571 | }
572 | }
573 |
574 | ///
575 | /// Gets a logger for the specified type.
576 | ///
577 | /// The type whose name will be used for the logger.
578 | /// An instance of
579 | #if LIBLOG_PUBLIC
580 | public
581 | #else
582 | internal
583 | #endif
584 | static ILog For()
585 | {
586 | return GetLogger(typeof(T));
587 | }
588 |
589 | #if !LIBLOG_PORTABLE
590 | ///
591 | /// Gets a logger for the current class.
592 | ///
593 | /// An instance of
594 | [MethodImpl(MethodImplOptions.NoInlining)]
595 | #if LIBLOG_PUBLIC
596 | public
597 | #else
598 | internal
599 | #endif
600 | static ILog GetCurrentClassLogger()
601 | {
602 | var stackFrame = new StackFrame(1, false);
603 | return GetLogger(stackFrame.GetMethod().DeclaringType);
604 | }
605 | #endif
606 |
607 | ///
608 | /// Gets a logger for the specified type.
609 | ///
610 | /// The type whose name will be used for the logger.
611 | /// If the type is null then this name will be used as the log name instead
612 | /// An instance of
613 | #if LIBLOG_PUBLIC
614 | public
615 | #else
616 | internal
617 | #endif
618 | static ILog GetLogger(Type type, string fallbackTypeName = "System.Object")
619 | {
620 | // If the type passed in is null then fallback to the type name specified
621 | return GetLogger(type != null ? type.FullName : fallbackTypeName);
622 | }
623 |
624 | ///
625 | /// Gets a logger with the specified name.
626 | ///
627 | /// The name.
628 | /// An instance of
629 | #if LIBLOG_PUBLIC
630 | public
631 | #else
632 | internal
633 | #endif
634 | static ILog GetLogger(string name)
635 | {
636 | ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider();
637 | return logProvider == null
638 | ? NoOpLogger.Instance
639 | : (ILog)new LoggerExecutionWrapper(logProvider.GetLogger(name), () => IsDisabled);
640 | }
641 |
642 | ///
643 | /// Opens a nested diagnostics context.
644 | ///
645 | /// A message.
646 | /// An that closes context when disposed.
647 | [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SetCurrentLogProvider")]
648 | #if LIBLOG_PUBLIC
649 | public
650 | #else
651 | internal
652 | #endif
653 | static IDisposable OpenNestedContext(string message)
654 | {
655 | ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider();
656 |
657 | return logProvider == null
658 | ? new DisposableAction(() => { })
659 | : logProvider.OpenNestedContext(message);
660 | }
661 |
662 | ///
663 | /// Opens a mapped diagnostics context.
664 | ///
665 | /// A key.
666 | /// A value.
667 | /// An that closes context when disposed.
668 | [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "SetCurrentLogProvider")]
669 | #if LIBLOG_PUBLIC
670 | public
671 | #else
672 | internal
673 | #endif
674 | static IDisposable OpenMappedContext(string key, object value, bool destructure = false)
675 | {
676 | ILogProvider logProvider = CurrentLogProvider ?? ResolveLogProvider();
677 |
678 | return logProvider == null
679 | ? new DisposableAction(() => { })
680 | : logProvider.OpenMappedContext(key, value, destructure);
681 | }
682 | #endif
683 |
684 | #if LIBLOG_PROVIDERS_ONLY
685 | private
686 | #else
687 | internal
688 | #endif
689 | delegate bool IsLoggerAvailable();
690 |
691 | #if LIBLOG_PROVIDERS_ONLY
692 | private
693 | #else
694 | internal
695 | #endif
696 | delegate ILogProvider CreateLogProvider();
697 |
698 | #if LIBLOG_PROVIDERS_ONLY
699 | private
700 | #else
701 | internal
702 | #endif
703 | static readonly List> LogProviderResolvers =
704 | new List>
705 | {
706 | new Tuple(SerilogLogProvider.IsLoggerAvailable, () => new SerilogLogProvider()),
707 | new Tuple(NLogLogProvider.IsLoggerAvailable, () => new NLogLogProvider()),
708 | new Tuple(Log4NetLogProvider.IsLoggerAvailable, () => new Log4NetLogProvider()),
709 | new Tuple(EntLibLogProvider.IsLoggerAvailable, () => new EntLibLogProvider()),
710 | new Tuple(LoupeLogProvider.IsLoggerAvailable, () => new LoupeLogProvider()),
711 | };
712 |
713 | #if !LIBLOG_PROVIDERS_ONLY
714 | private static void RaiseOnCurrentLogProviderSet()
715 | {
716 | if (s_onCurrentLogProviderSet != null)
717 | {
718 | s_onCurrentLogProviderSet(s_currentLogProvider);
719 | }
720 | }
721 | #endif
722 |
723 | internal static ILogProvider ResolveLogProvider()
724 | {
725 | return s_resolvedLogProvider.Value;
726 | }
727 |
728 | [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Console.WriteLine(System.String,System.Object,System.Object)")]
729 | [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
730 | internal static ILogProvider ForceResolveLogProvider()
731 | {
732 | try
733 | {
734 | foreach (var providerResolver in LogProviderResolvers)
735 | {
736 | if (providerResolver.Item1())
737 | {
738 | return providerResolver.Item2();
739 | }
740 | }
741 | }
742 | catch (Exception ex)
743 | {
744 | #if LIBLOG_PORTABLE
745 | Debug.WriteLine(
746 | #else
747 | Console.WriteLine(
748 | #endif
749 | "Exception occurred resolving a log provider. Logging for this assembly {0} is disabled. {1}",
750 | typeof(LogProvider).GetAssemblyPortable().FullName,
751 | ex);
752 | }
753 | return null;
754 | }
755 |
756 | #if !LIBLOG_PROVIDERS_ONLY
757 | #if !LIBLOG_PORTABLE
758 | [ExcludeFromCodeCoverage]
759 | #endif
760 | internal class NoOpLogger : ILog
761 | {
762 | internal static readonly NoOpLogger Instance = new NoOpLogger();
763 |
764 | public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters)
765 | {
766 | return false;
767 | }
768 | }
769 | #endif
770 | }
771 |
772 | #if !LIBLOG_PROVIDERS_ONLY
773 | #if !LIBLOG_PORTABLE
774 | [ExcludeFromCodeCoverage]
775 | #endif
776 | internal class LoggerExecutionWrapper : ILog
777 | {
778 | private readonly Logger _logger;
779 | private readonly ICallSiteExtension _callsiteLogger;
780 | private readonly Func _getIsDisabled;
781 | internal const string FailedToGenerateLogMessage = "Failed to generate log message";
782 |
783 | Func _lastExtensionMethod;
784 |
785 | internal LoggerExecutionWrapper(Logger logger, Func getIsDisabled = null)
786 | {
787 | _logger = logger;
788 | _callsiteLogger = new CallSiteExtension();
789 | _getIsDisabled = getIsDisabled ?? (() => false);
790 | }
791 |
792 | internal Logger WrappedLogger
793 | {
794 | get { return _logger; }
795 | }
796 |
797 | [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
798 | public bool Log(LogLevel logLevel, Func messageFunc, Exception exception = null, params object[] formatParameters)
799 | {
800 | if (_getIsDisabled())
801 | {
802 | return false;
803 | }
804 | if (messageFunc == null)
805 | {
806 | return _logger(logLevel, null);
807 | }
808 |
809 | #if !LIBLOG_PORTABLE
810 | // Callsite HACK - Using the messageFunc to provide the callsite-logger-type
811 | var lastExtensionMethod = _lastExtensionMethod;
812 | if (lastExtensionMethod == null || !lastExtensionMethod.Equals(messageFunc))
813 | {
814 | // Callsite HACK - Cache the last validated messageFunc as Equals is faster than type-check
815 | lastExtensionMethod = null;
816 | var methodType = messageFunc.Method.DeclaringType;
817 | if (methodType == typeof(LogExtensions) || (methodType != null && methodType.DeclaringType == typeof(LogExtensions)))
818 | {
819 | lastExtensionMethod = messageFunc;
820 | }
821 | }
822 |
823 | if (lastExtensionMethod != null)
824 | {
825 | // Callsite HACK - LogExtensions has called virtual ILog interface method to get here, callsite-stack is good
826 | _lastExtensionMethod = lastExtensionMethod;
827 | return _logger(logLevel, LogExtensions.WrapLogSafeInternal(this, messageFunc), exception, formatParameters);
828 | }
829 | else
830 | #endif
831 | {
832 | Func wrappedMessageFunc = () =>
833 | {
834 | try
835 | {
836 | return messageFunc();
837 | }
838 | catch (Exception ex)
839 | {
840 | _logger(LogLevel.Error, () => FailedToGenerateLogMessage, ex);
841 | }
842 | return null;
843 | };
844 |
845 | // Callsite HACK - Need to ensure proper callsite stack without inlining, so calling the logger within a virtual interface method
846 | return _callsiteLogger.Log(_logger, logLevel, wrappedMessageFunc, exception, formatParameters);
847 | }
848 | }
849 |
850 | interface ICallSiteExtension
851 | {
852 | bool Log(Logger logger, LogLevel logLevel, Func messageFunc, Exception exception, object[] formatParameters);
853 | }
854 |
855 | class CallSiteExtension : ICallSiteExtension
856 | {
857 | bool ICallSiteExtension.Log(Logger logger, LogLevel logLevel, Func messageFunc, Exception exception, object[] formatParameters)
858 | {
859 | return logger(logLevel, messageFunc, exception, formatParameters);
860 | }
861 | }
862 | }
863 | #endif
864 | }
865 |
866 | #if LIBLOG_PROVIDERS_ONLY
867 | namespace YourRootNamespace.LibLog.LogProviders
868 | #else
869 | namespace SRTM.Logging.LogProviders
870 | #endif
871 | {
872 | using System;
873 | using System.Collections.Generic;
874 | using System.Diagnostics.CodeAnalysis;
875 | #if !LIBLOG_PORTABLE
876 | using System.Diagnostics;
877 | #endif
878 | using System.Globalization;
879 | using System.Linq;
880 | using System.Linq.Expressions;
881 | using System.Reflection;
882 | #if !LIBLOG_PORTABLE
883 | using System.Text;
884 | #endif
885 | using System.Text.RegularExpressions;
886 |
887 | #if !LIBLOG_PORTABLE
888 | [ExcludeFromCodeCoverage]
889 | #endif
890 | internal abstract class LogProviderBase : ILogProvider
891 | {
892 | protected delegate IDisposable OpenNdc(string message);
893 | protected delegate IDisposable OpenMdc(string key, object value, bool destructure);
894 |
895 | private readonly Lazy _lazyOpenNdcMethod;
896 | private readonly Lazy _lazyOpenMdcMethod;
897 | private static readonly IDisposable NoopDisposableInstance = new DisposableAction();
898 |
899 | protected LogProviderBase()
900 | {
901 | _lazyOpenNdcMethod
902 | = new Lazy(GetOpenNdcMethod);
903 | _lazyOpenMdcMethod
904 | = new Lazy(GetOpenMdcMethod);
905 | }
906 |
907 | public abstract Logger GetLogger(string name);
908 |
909 | public IDisposable OpenNestedContext(string message)
910 | {
911 | return _lazyOpenNdcMethod.Value(message);
912 | }
913 |
914 | public IDisposable OpenMappedContext(string key, object value, bool destructure = false)
915 | {
916 | return _lazyOpenMdcMethod.Value(key, value, destructure);
917 | }
918 |
919 | protected virtual OpenNdc GetOpenNdcMethod()
920 | {
921 | return _ => NoopDisposableInstance;
922 | }
923 |
924 | protected virtual OpenMdc GetOpenMdcMethod()
925 | {
926 | return (_, __, ___) => NoopDisposableInstance;
927 | }
928 | }
929 |
930 | #if !LIBLOG_PORTABLE
931 | [ExcludeFromCodeCoverage]
932 | #endif
933 | internal class NLogLogProvider : LogProviderBase
934 | {
935 | private readonly Func _getLoggerByNameDelegate;
936 | private static bool s_providerIsAvailableOverride = true;
937 |
938 | [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogManager")]
939 | [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "NLog")]
940 | public NLogLogProvider()
941 | {
942 | if (!IsLoggerAvailable())
943 | {
944 | throw new InvalidOperationException("NLog.LogManager not found");
945 | }
946 | _getLoggerByNameDelegate = GetGetLoggerMethodCall();
947 | }
948 |
949 | public static bool ProviderIsAvailableOverride
950 | {
951 | get { return s_providerIsAvailableOverride; }
952 | set { s_providerIsAvailableOverride = value; }
953 | }
954 |
955 | public override Logger GetLogger(string name)
956 | {
957 | return new NLogLogger(_getLoggerByNameDelegate(name)).Log;
958 | }
959 |
960 | public static bool IsLoggerAvailable()
961 | {
962 | return ProviderIsAvailableOverride && GetLogManagerType() != null;
963 | }
964 |
965 | protected override OpenNdc GetOpenNdcMethod()
966 | {
967 | Type ndcContextType = Type.GetType("NLog.NestedDiagnosticsContext, NLog");
968 | MethodInfo pushMethod = ndcContextType.GetMethodPortable("Push", typeof(string));
969 | ParameterExpression messageParam = Expression.Parameter(typeof(string), "message");
970 | MethodCallExpression pushMethodCall = Expression.Call(null, pushMethod, messageParam);
971 | return Expression.Lambda(pushMethodCall, messageParam).Compile();
972 | }
973 |
974 | protected override OpenMdc GetOpenMdcMethod()
975 | {
976 | Type mdcContextType = Type.GetType("NLog.MappedDiagnosticsContext, NLog");
977 |
978 | MethodInfo setMethod = mdcContextType.GetMethodPortable("Set", typeof(string), typeof(string));
979 | MethodInfo removeMethod = mdcContextType.GetMethodPortable("Remove", typeof(string));
980 | ParameterExpression keyParam = Expression.Parameter(typeof(string), "key");
981 | ParameterExpression valueParam = Expression.Parameter(typeof(string), "value");
982 |
983 | MethodCallExpression setMethodCall = Expression.Call(null, setMethod, keyParam, valueParam);
984 | MethodCallExpression removeMethodCall = Expression.Call(null, removeMethod, keyParam);
985 |
986 | Action set = Expression
987 | .Lambda>(setMethodCall, keyParam, valueParam)
988 | .Compile();
989 | Action remove = Expression
990 | .Lambda>(removeMethodCall, keyParam)
991 | .Compile();
992 |
993 | return (key, value, _) =>
994 | {
995 | set(key, value.ToString());
996 | return new DisposableAction(() => remove(key));
997 | };
998 | }
999 |
1000 | private static Type GetLogManagerType()
1001 | {
1002 | return Type.GetType("NLog.LogManager, NLog");
1003 | }
1004 |
1005 | private static Func GetGetLoggerMethodCall()
1006 | {
1007 | Type logManagerType = GetLogManagerType();
1008 | MethodInfo method = logManagerType.GetMethodPortable("GetLogger", typeof(string));
1009 | ParameterExpression nameParam = Expression.Parameter(typeof(string), "name");
1010 | MethodCallExpression methodCall = Expression.Call(null, method, nameParam);
1011 | return Expression.Lambda>(methodCall, nameParam).Compile();
1012 | }
1013 |
1014 | #if !LIBLOG_PORTABLE
1015 | [ExcludeFromCodeCoverage]
1016 | #endif
1017 | internal class NLogLogger
1018 | {
1019 | private readonly dynamic _logger;
1020 |
1021 | private static Func _logEventInfoFact;
1022 |
1023 | private static readonly object _levelTrace;
1024 | private static readonly object _levelDebug;
1025 | private static readonly object _levelInfo;
1026 | private static readonly object _levelWarn;
1027 | private static readonly object _levelError;
1028 | private static readonly object _levelFatal;
1029 |
1030 | static NLogLogger()
1031 | {
1032 | try
1033 | {
1034 | var logEventLevelType = Type.GetType("NLog.LogLevel, NLog");
1035 | if (logEventLevelType == null)
1036 | {
1037 | throw new InvalidOperationException("Type NLog.LogLevel was not found.");
1038 | }
1039 |
1040 | var levelFields = logEventLevelType.GetFieldsPortable().ToList();
1041 | _levelTrace = levelFields.First(x => x.Name == "Trace").GetValue(null);
1042 | _levelDebug = levelFields.First(x => x.Name == "Debug").GetValue(null);
1043 | _levelInfo = levelFields.First(x => x.Name == "Info").GetValue(null);
1044 | _levelWarn = levelFields.First(x => x.Name == "Warn").GetValue(null);
1045 | _levelError = levelFields.First(x => x.Name == "Error").GetValue(null);
1046 | _levelFatal = levelFields.First(x => x.Name == "Fatal").GetValue(null);
1047 |
1048 | var logEventInfoType = Type.GetType("NLog.LogEventInfo, NLog");
1049 | if (logEventInfoType == null)
1050 | {
1051 | throw new InvalidOperationException("Type NLog.LogEventInfo was not found.");
1052 | }
1053 |
1054 | ConstructorInfo loggingEventConstructor =
1055 | logEventInfoType.GetConstructorPortable(logEventLevelType, typeof(string), typeof(IFormatProvider), typeof(string), typeof(object[]), typeof(Exception));
1056 |
1057 | ParameterExpression loggerNameParam = Expression.Parameter(typeof(string));
1058 | ParameterExpression levelParam = Expression.Parameter(typeof(object));
1059 | ParameterExpression messageParam = Expression.Parameter(typeof(string));
1060 | ParameterExpression exceptionParam = Expression.Parameter(typeof(Exception));
1061 | UnaryExpression levelCast = Expression.Convert(levelParam, logEventLevelType);
1062 |
1063 | NewExpression newLoggingEventExpression =
1064 | Expression.New(loggingEventConstructor,
1065 | levelCast,
1066 | loggerNameParam,
1067 | Expression.Constant(null, typeof(IFormatProvider)),
1068 | messageParam,
1069 | Expression.Constant(null, typeof(object[])),
1070 | exceptionParam
1071 | );
1072 |
1073 | _logEventInfoFact = Expression.Lambda>(newLoggingEventExpression,
1074 | loggerNameParam, levelParam, messageParam, exceptionParam).Compile();
1075 | }
1076 | catch { }
1077 | }
1078 |
1079 | internal NLogLogger(dynamic logger)
1080 | {
1081 | _logger = logger;
1082 | }
1083 |
1084 | [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
1085 | public bool Log(LogLevel logLevel, Func messageFunc, Exception exception, params object[] formatParameters)
1086 | {
1087 | if (messageFunc == null)
1088 | {
1089 | return IsLogLevelEnable(logLevel);
1090 | }
1091 |
1092 | var callsiteMessageFunc = messageFunc;
1093 | messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters);
1094 |
1095 | if (_logEventInfoFact != null)
1096 | {
1097 | if (IsLogLevelEnable(logLevel))
1098 | {
1099 | Type callsiteLoggerType = typeof(NLogLogger);
1100 | #if !LIBLOG_PORTABLE
1101 | // Callsite HACK - Extract the callsite-logger-type from the messageFunc
1102 | var methodType = callsiteMessageFunc.Method.DeclaringType;
1103 | if (methodType == typeof(LogExtensions) || (methodType != null && methodType.DeclaringType == typeof(LogExtensions)))
1104 | {
1105 | callsiteLoggerType = typeof(LogExtensions);
1106 | }
1107 | else if (methodType == typeof(LoggerExecutionWrapper) || (methodType != null && methodType.DeclaringType == typeof(LoggerExecutionWrapper)))
1108 | {
1109 | callsiteLoggerType = typeof(LoggerExecutionWrapper);
1110 | }
1111 | #endif
1112 | var nlogLevel = this.TranslateLevel(logLevel);
1113 | var nlogEvent = _logEventInfoFact(_logger.Name, nlogLevel, messageFunc(), exception);
1114 | _logger.Log(callsiteLoggerType, nlogEvent);
1115 | return true;
1116 | }
1117 | return false;
1118 | }
1119 |
1120 | if (exception != null)
1121 | {
1122 | return LogException(logLevel, messageFunc, exception);
1123 | }
1124 |
1125 | switch (logLevel)
1126 | {
1127 | case LogLevel.Debug:
1128 | if (_logger.IsDebugEnabled)
1129 | {
1130 | _logger.Debug(messageFunc());
1131 | return true;
1132 | }
1133 | break;
1134 | case LogLevel.Info:
1135 | if (_logger.IsInfoEnabled)
1136 | {
1137 | _logger.Info(messageFunc());
1138 | return true;
1139 | }
1140 | break;
1141 | case LogLevel.Warn:
1142 | if (_logger.IsWarnEnabled)
1143 | {
1144 | _logger.Warn(messageFunc());
1145 | return true;
1146 | }
1147 | break;
1148 | case LogLevel.Error:
1149 | if (_logger.IsErrorEnabled)
1150 | {
1151 | _logger.Error(messageFunc());
1152 | return true;
1153 | }
1154 | break;
1155 | case LogLevel.Fatal:
1156 | if (_logger.IsFatalEnabled)
1157 | {
1158 | _logger.Fatal(messageFunc());
1159 | return true;
1160 | }
1161 | break;
1162 | default:
1163 | if (_logger.IsTraceEnabled)
1164 | {
1165 | _logger.Trace(messageFunc());
1166 | return true;
1167 | }
1168 | break;
1169 | }
1170 | return false;
1171 | }
1172 |
1173 | [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
1174 | private bool LogException(LogLevel logLevel, Func messageFunc, Exception exception)
1175 | {
1176 | switch (logLevel)
1177 | {
1178 | case LogLevel.Debug:
1179 | if (_logger.IsDebugEnabled)
1180 | {
1181 | _logger.DebugException(messageFunc(), exception);
1182 | return true;
1183 | }
1184 | break;
1185 | case LogLevel.Info:
1186 | if (_logger.IsInfoEnabled)
1187 | {
1188 | _logger.InfoException(messageFunc(), exception);
1189 | return true;
1190 | }
1191 | break;
1192 | case LogLevel.Warn:
1193 | if (_logger.IsWarnEnabled)
1194 | {
1195 | _logger.WarnException(messageFunc(), exception);
1196 | return true;
1197 | }
1198 | break;
1199 | case LogLevel.Error:
1200 | if (_logger.IsErrorEnabled)
1201 | {
1202 | _logger.ErrorException(messageFunc(), exception);
1203 | return true;
1204 | }
1205 | break;
1206 | case LogLevel.Fatal:
1207 | if (_logger.IsFatalEnabled)
1208 | {
1209 | _logger.FatalException(messageFunc(), exception);
1210 | return true;
1211 | }
1212 | break;
1213 | default:
1214 | if (_logger.IsTraceEnabled)
1215 | {
1216 | _logger.TraceException(messageFunc(), exception);
1217 | return true;
1218 | }
1219 | break;
1220 | }
1221 | return false;
1222 | }
1223 |
1224 | private bool IsLogLevelEnable(LogLevel logLevel)
1225 | {
1226 | switch (logLevel)
1227 | {
1228 | case LogLevel.Debug:
1229 | return _logger.IsDebugEnabled;
1230 | case LogLevel.Info:
1231 | return _logger.IsInfoEnabled;
1232 | case LogLevel.Warn:
1233 | return _logger.IsWarnEnabled;
1234 | case LogLevel.Error:
1235 | return _logger.IsErrorEnabled;
1236 | case LogLevel.Fatal:
1237 | return _logger.IsFatalEnabled;
1238 | default:
1239 | return _logger.IsTraceEnabled;
1240 | }
1241 | }
1242 |
1243 | private object TranslateLevel(LogLevel logLevel)
1244 | {
1245 | switch (logLevel)
1246 | {
1247 | case LogLevel.Trace:
1248 | return _levelTrace;
1249 | case LogLevel.Debug:
1250 | return _levelDebug;
1251 | case LogLevel.Info:
1252 | return _levelInfo;
1253 | case LogLevel.Warn:
1254 | return _levelWarn;
1255 | case LogLevel.Error:
1256 | return _levelError;
1257 | case LogLevel.Fatal:
1258 | return _levelFatal;
1259 | default:
1260 | throw new ArgumentOutOfRangeException("logLevel", logLevel, null);
1261 | }
1262 | }
1263 | }
1264 | }
1265 |
1266 | #if !LIBLOG_PORTABLE
1267 | [ExcludeFromCodeCoverage]
1268 | #endif
1269 | internal class Log4NetLogProvider : LogProviderBase
1270 | {
1271 | private readonly Func _getLoggerByNameDelegate;
1272 | private static bool s_providerIsAvailableOverride = true;
1273 |
1274 | [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "LogManager")]
1275 | public Log4NetLogProvider()
1276 | {
1277 | if (!IsLoggerAvailable())
1278 | {
1279 | throw new InvalidOperationException("log4net.LogManager not found");
1280 | }
1281 | _getLoggerByNameDelegate = GetGetLoggerMethodCall();
1282 | }
1283 |
1284 | public static bool ProviderIsAvailableOverride
1285 | {
1286 | get { return s_providerIsAvailableOverride; }
1287 | set { s_providerIsAvailableOverride = value; }
1288 | }
1289 |
1290 | public override Logger GetLogger(string name)
1291 | {
1292 | return new Log4NetLogger(_getLoggerByNameDelegate(name)).Log;
1293 | }
1294 |
1295 | internal static bool IsLoggerAvailable()
1296 | {
1297 | return ProviderIsAvailableOverride && GetLogManagerType() != null;
1298 | }
1299 |
1300 | protected override OpenNdc GetOpenNdcMethod()
1301 | {
1302 | Type logicalThreadContextType = Type.GetType("log4net.LogicalThreadContext, log4net");
1303 | PropertyInfo stacksProperty = logicalThreadContextType.GetPropertyPortable("Stacks");
1304 | Type logicalThreadContextStacksType = stacksProperty.PropertyType;
1305 | PropertyInfo stacksIndexerProperty = logicalThreadContextStacksType.GetPropertyPortable("Item");
1306 | Type stackType = stacksIndexerProperty.PropertyType;
1307 | MethodInfo pushMethod = stackType.GetMethodPortable("Push");
1308 |
1309 | ParameterExpression messageParameter =
1310 | Expression.Parameter(typeof(string), "message");
1311 |
1312 | // message => LogicalThreadContext.Stacks.Item["NDC"].Push(message);
1313 | MethodCallExpression callPushBody =
1314 | Expression.Call(
1315 | Expression.Property(Expression.Property(null, stacksProperty),
1316 | stacksIndexerProperty,
1317 | Expression.Constant("NDC")),
1318 | pushMethod,
1319 | messageParameter);
1320 |
1321 | OpenNdc result =
1322 | Expression.Lambda(callPushBody, messageParameter)
1323 | .Compile();
1324 |
1325 | return result;
1326 | }
1327 |
1328 | protected override OpenMdc GetOpenMdcMethod()
1329 | {
1330 | Type logicalThreadContextType = Type.GetType("log4net.LogicalThreadContext, log4net");
1331 | PropertyInfo propertiesProperty = logicalThreadContextType.GetPropertyPortable("Properties");
1332 | Type logicalThreadContextPropertiesType = propertiesProperty.PropertyType;
1333 | PropertyInfo propertiesIndexerProperty = logicalThreadContextPropertiesType.GetPropertyPortable("Item");
1334 |
1335 | MethodInfo removeMethod = logicalThreadContextPropertiesType.GetMethodPortable("Remove");
1336 |
1337 | ParameterExpression keyParam = Expression.Parameter(typeof(string), "key");
1338 | ParameterExpression valueParam = Expression.Parameter(typeof(string), "value");
1339 |
1340 | MemberExpression propertiesExpression = Expression.Property(null, propertiesProperty);
1341 |
1342 | // (key, value) => LogicalThreadContext.Properties.Item[key] = value;
1343 | BinaryExpression setProperties = Expression.Assign(Expression.Property(propertiesExpression, propertiesIndexerProperty, keyParam), valueParam);
1344 |
1345 | // key => LogicalThreadContext.Properties.Remove(key);
1346 | MethodCallExpression removeMethodCall = Expression.Call(propertiesExpression, removeMethod, keyParam);
1347 |
1348 | Action set = Expression
1349 | .Lambda>(setProperties, keyParam, valueParam)
1350 | .Compile();
1351 |
1352 | Action remove = Expression
1353 | .Lambda>(removeMethodCall, keyParam)
1354 | .Compile();
1355 |
1356 | return (key, value, _) =>
1357 | {
1358 | set(key, value.ToString());
1359 | return new DisposableAction(() => remove(key));
1360 | };
1361 | }
1362 |
1363 | private static Type GetLogManagerType()
1364 | {
1365 | return Type.GetType("log4net.LogManager, log4net");
1366 | }
1367 |
1368 | private static Func GetGetLoggerMethodCall()
1369 | {
1370 | Type logManagerType = GetLogManagerType();
1371 | MethodInfo method = logManagerType.GetMethodPortable("GetLogger", typeof(string));
1372 | ParameterExpression nameParam = Expression.Parameter(typeof(string), "name");
1373 | MethodCallExpression methodCall = Expression.Call(null, method, nameParam);
1374 | return Expression.Lambda>(methodCall, nameParam).Compile();
1375 | }
1376 |
1377 | #if !LIBLOG_PORTABLE
1378 | [ExcludeFromCodeCoverage]
1379 | #endif
1380 | internal class Log4NetLogger
1381 | {
1382 | private readonly dynamic _logger;
1383 | private static Type s_callerStackBoundaryType;
1384 | private static readonly object CallerStackBoundaryTypeSync = new object();
1385 |
1386 | private static readonly object _levelDebug;
1387 | private static readonly object _levelInfo;
1388 | private static readonly object _levelWarn;
1389 | private static readonly object _levelError;
1390 | private static readonly object _levelFatal;
1391 | private static readonly Func