DotSpatial (C#) re-projections - 'InitializeExternalGrids' causes .Net to 'freeze' - c#

I am trying to apply an NTv2 gridshift package (OSTN15 from the UK Ordnance Survey) to some DotSpatial re-projections. This is the code I have so far:
const string Proj4_4326 = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs";
const string OSTN15 = #"+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +units=m +no_defs +nadgrids=D:\directory\OSTN15_NTv2_OSGBtoETRS.gsb";
static void Main(string[] args)
{
double[] polygonPoints = new double[2] { 530624.974, 178388.464 };
double[] zp = new double[10];
ProjectionInfo srcProjection = ProjectionInfo.FromProj4String(OSTN15);
ProjectionInfo desProjection = ProjectionInfo.FromProj4String(Proj4_4326);
GridShift.InitializeExternalGrids(#"D:\directory\OSTN15-NTv2", false);
Reproject.ReprojectPoints(polygonPoints, zp, srcProjection, desProjection, 0, 1);
}
The code gets to the 'Gridshift.InitializeExternalGrids' and 'hangs'. It appears to be processing but after 40 minutes it has built up to 2G RAM used and about 25% CPU usage constant.
I'm assuming that it should not take this long to initialise the grids and that something is going wrong.
I am using DotSpatial.Projections 1.9.0 added using NuGet, there is nothing else in the project other than this code, it is a .NET Framework 4.7.2 console app, x64 platform target (I tried x86 also).
Has anyone else had this issue or can offer any advice .
Thanks.

Related

.NET Core 3.1 v .NET 6.0

I got all excited on the release of Visual Studio 2022, C# 10 and .NET 6.0 and downloaded and installed the community edition and tested a project I am working on. I changed the target framework to 6.0 and performed a clean build. Great, everything built as expected.
So, onwards and upwards and ran the project. The very first test failed. I must say I was surprised. I started digging around and was really surprised to find a difference between .NET Core 3.1 and .NET 6.0.
Here is a sample program:
public class Program
{
public static readonly string CTCPDelimiterString = "\x0001";
public static readonly char CTCPDelimiterChar = '\x0001';
public static void Main(string[] args)
{
string text = "!sampletext";
Console.Write(" Using a char: ");
if (text.StartsWith(CTCPDelimiterChar) && text.EndsWith(CTCPDelimiterChar))
{
Console.WriteLine("got CTCP delimiters");
}
else
{
Console.WriteLine("did not get CTCP delimiters");
}
Console.Write("Using a string: ");
if (text.StartsWith(CTCPDelimiterString) && text.EndsWith(CTCPDelimiterString))
{
Console.WriteLine("got CTCP delimiters");
}
else
{
Console.WriteLine("did not get CTCP delimiters");
}
}
}
Using a target framework of 'netcoreapp3.1' I got the following output:
Using a char: did not get CTCP delimiters
Using a string: did not get CTCP delimiters
Using a target framework of 'net6.0' I got the following output:
Using a char: did not get CTCP delimiters
Using a string: got CTCP delimiters
So, I can only assume that it is a unicode setting but I cannot find it anywhere (if there is one). My understanding is that all strings are UTF16 but why the difference between frameworks.
And yes, I can see the bug in my code, it should be a char anyway but it was working fine using 'netcoreapp3.1'.
Can anyone shed some light on this please.
After .Net Core 3, you must highlight your comparison mode by StringComparison code.
change
if (text.StartsWith(CTCPDelimiterString) && text.EndsWith(CTCPDelimiterString))
with
if (text.StartsWith(CTCPDelimiterString, StringComparison.Ordinal) && text.EndsWith(CTCPDelimiterString, StringComparison.Ordinal))

ML.NET in Unity

I cannot figure out how to use ML.NET in Unity.
What I did:
Converted my project to be compatible with framework 4.x.
Converted api compatibility level to framework 4.x.
Made assets/plugins/ml folder and droped in Microsoft.ML apis with corresponding xmls.
Marked all ml.dlls platform settings to be only 86_64 compatible (this was redundant).
I can now:
Call ML apis and create MlContext, TextLoader, and do the training of a model. When a model is trained I can also evaluate the trained model, but...
I cannot:
When trying to get a prediction out of the model I get an error:
github comment on issue from 28.12.18 (there is also a whole project attached there, you can see the code there)
The same code works in visual studio solution.
public float TestSinglePrediction(List<double> signal, MLContext mlContext, string modelPath)
{
ITransformer loadedModel;
using (var stream = new FileStream(modelPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
loadedModel = mlContext.Model.Load(stream);
}
var predictionFunction = loadedModel.MakePredictionFunction<AbstractSignal, PredictedRfd>(mlContext);
var abstractSignal = new AbstractSignal()
{
Sig1 = (float)signal[0],
Sig2 = (float)signal[1],
Sig3 = (float)signal[2],
Sig4 = (float)signal[3],
Sig5 = (float)signal[4],
Sig6 = (float)signal[5],
Sig7 = (float)signal[6],
Sig8 = (float)signal[7],
Sig9 = (float)signal[8],
Sig10 = (float)signal[9],
Sig11 = (float)signal[10],
Sig12 = (float)signal[11],
Sig13 = (float)signal[12],
Sig14 = (float)signal[13],
Sig15 = (float)signal[14],
Sig16 = (float)signal[15],
Sig17 = (float)signal[16],
Sig18 = (float)signal[17],
Sig19 = (float)signal[18],
Sig20 = (float)signal[19],
RfdX = 0
};
var prediction = predictionFunction.Predict(abstractSignal);
return prediction.RfdX;
}
This is the method that returns an error line:
var predictionFunction = loadedModel.MakePredictionFunction<AbstractSignal, PredictedRfd>(mlContext);
Starting with Unity 2018.1, unity can target .net 4.x. So you would need to set the .net version to .NET 4.x Equivalent, or .net standard 2.0 (https://blogs.unity3d.com/2018/03/28/updated-scripting-runtime-in-unity-2018-1-what-does-the-future-hold/) and make sure you add your dll to the project as a reference in visual studio. If you don't add it as a reference, then visual sudio doesn't know about it.
As Nick said in his post**, it should work with Unity if you follow those steps.
However, at the time I am writing this post, the ML.NET team has not yet done comprehensive testing with Unity, so it's not completely surprising that it's not working out of the box. This issue has been opened on the ML.NET Github repository. I suggest keeping an eye on that issue for the status of Unity support.
** Nick:
Starting with Unity 2018.1, unity can target .net 4.x. So you would need to set the .net version to .NET 4.x Equivalent, or .net standard 2.0 (https://blogs.unity3d.com/2018/03/28/updated-scripting-runtime-in-unity-2018-1-what-does-the-future-hold/) and make sure you add your dll to the project as a reference in visual studio. If you don't add it as a reference, then visual sudio doesn't know about it.
As follows is a bit modyfied Iris Example from https://learn.microsoft.com/en-us/dotnet/machine-learning/tutorials/iris-clustering (that one does not work anymore due to some ML API changes)
First make sure that you have the latest .net version installed and that your Unity version is at least 2019.2.0f1 (this was a preview version) or higher.
Creste a new unity project. Create a Plugins folder inside your Assets folder. Import all ML .Net APIs into that folder (Might be a foolish thing to do, but I have forehand created a visual studio soution and added all those APIs to that solution via nuget, and than just copied those dll files to Assets/Plugins folder in my unity project).
In Assets folder create an Data folder and paste iris.data file from https://github.com/dotnet/machinelearning/blob/master/test/data/iris.data into it.
Create a script named MLuTest and paste into it the following code:
public class MLuTest : MonoBehaviour{
static readonly string _dataPath = Path.Combine(Environment.CurrentDirectory, "Assets", "Data", "iris.data");
static readonly string _modelPath = Path.Combine(Environment.CurrentDirectory, "Assets", "Data", "IrisClusteringModel.zip");
MLContext mlContext;
void Start()
{
Debug.Log("starting...");
mlContext = new MLContext(seed: 0);
IDataView dataView = mlContext.Data.ReadFromTextFile<IrisData>(_dataPath, hasHeader: false, separatorChar: ',');
string featuresColumnName = "Features";
var pipeline = mlContext.Transforms
.Concatenate(featuresColumnName, "SepalLength", "SepalWidth", "PetalLength", "PetalWidth")
.Append(mlContext.Clustering.Trainers.KMeans(featuresColumnName, clustersCount: 3));//read and format flowery data
var model = pipeline.Fit(dataView);//train
using (var fileStream = new FileStream(_modelPath, FileMode.Create, FileAccess.Write, FileShare.Write))//save trained model
{
mlContext.Model.Save(model, fileStream);
}
var predictor = mlContext.Model.CreatePredictionEngine<IrisData, ClusterPrediction>(model);//predict
IrisData Setosa = new IrisData
{
SepalLength = 5.1f,
SepalWidth = 3.5f,
PetalLength = 1.4f,
PetalWidth = 0.2f
};
Debug.Log(predictor.Predict(Setosa).PredictedClusterId);
Debug.Log("...done predicting, now do what u like with it");
}
}
public class IrisData
{
[LoadColumn(0)]
public float SepalLength;
[LoadColumn(1)]
public float SepalWidth;
[LoadColumn(2)]
public float PetalLength;
[LoadColumn(3)]
public float PetalWidth;
}
public class ClusterPrediction
{
[ColumnName("PredictedLabel")]
public uint PredictedClusterId;
[ColumnName("Score")]
public float[] Distances;
}
This should work right out of the box ... well it did for me. Where you could mess up is when getting api files, they could be different version from mine or just some .net framework compatible. So get the content of my Plugins folder (mind you all those apis may not be necesery, do the cherrypicking yourselve):https://github.com/dotnet/machinelearning/issues/1886
It used to be (in previous unity versions) that some player settings had to be changed but i did not have to do it. But anhoo here are mine:
I hope this helps, since Unity update 19.2 I have not had any problems mentioned in previous posts in this thread.

.Net Core Library in .Net Standard: Core Long Path Handling

How would one use a .Net Core library in a regular .Net application? I came across this Hanselman article that explains I should be using a .Net Standard library. Problem is, my code only works in a Core Library (afaik).
The below method works in Core but not in a Standard library. It appears that Core handles long paths (over the 250 char limit) better than regular .Net solutions (Background).
Is there a way to use a Core library in a regular .Net library?
private static List<FileInfo> FastGetFileList(string myBaseDirectory, string searchString = "*.*")
{
var dirInfo = new DirectoryInfo(myBaseDirectory);
return dirInfo.EnumerateDirectories()
.AsParallel()
.SelectMany(di => di.EnumerateFiles(searchString, SearchOption.AllDirectories)).ToList()
.Union(dirInfo.EnumerateFiles(searchString)).ToList();
}
Full Solution for Testing
This will fail in a regular console app and also doesn't work when called from a .Net Standard 2.0 library. In a Core console app, it will work.
private string path =
#"\\GREATER THAN 250 CHARACTERS NETWORK FOLDER LOCATION"
;
static void Main(string[] args)
{
var temp = FastGetFileList(path);
Console.WriteLine("Success");
Console.ReadLine();
}
private static List<FileInfo> FastGetFileList(string myBaseDirectory, string searchString = "*.*")
{
var dirInfo = new DirectoryInfo(myBaseDirectory);
return dirInfo.EnumerateDirectories()
.AsParallel()
.SelectMany(di => di.EnumerateFiles(searchString, SearchOption.AllDirectories)).ToList()
.Union(dirInfo.EnumerateFiles(searchString)).ToList();
}

Connecting from C# to Accumulo

I am new to working with Accumulo. I need to read/write data from a remote Accumulo through C#.
The only code sample/documentation for C#, I have found is -
Accumulo createBatchScanner range not working as expected
I attempted to compile the code in Xamarin Studio, on a Mac.
The issue I am encountering is with this line:
AccumuloProxy.Client client = new AccumuloProxy.Client(protocol);
Error CS0246: The type or namespace name AccumuloProxy' could not be found. Are you missingorg.apache.accumulo.proxy.thrift' using directive? (CS0246) (AccumuloIntegratorPrototype)
Where can I find the DLLs to add to my CSharp project related to AccumuloProxy client?
Is there a way I can generate the same?
Here is a code fragment:
namespace AccumuloIntegratorPrototype
{
class MainClass
{
static byte[] GetBytes(string str)
{
return Encoding.ASCII.GetBytes(str);
}
static string GetString(byte[] bytes)
{
return Encoding.ASCII.GetString(bytes);
}
public static void Main (string[] args)
{
try
{
/** connect **/
TTransport transport = new TSocket("xxx.xx.x.xx", 42424);
transport = new TFramedTransport(transport);
TCompactProtocol protocol = new TCompactProtocol(transport);
transport.Open();
AccumuloProxy.Client client = new AccumuloProxy.Client(protocol);
Thanks to all for the pointers.
Was able to complete my project.
These are my notes.
A. Versions:
Accumulo 1.5
Thrift 0.90
Mono 3.2.5
B. Strategy/option used to connect to Accumulo from C#:
Accumulo Proxy API
C. Accumulo Proxy with C# bindings:
Performed the following actions on a node running Accumulo
1. Installed Mono 3.2.5
2. Installed Thrift 0.90
3. Configured Accumulo proxy service
Modified the file $ACCUMULO_HOME/proxy/proxy.properties;
Specifically updated the instance name, and zookeeper
4. Started the proxy daemon-
${ACCUMULO_HOME}/bin/accumulo proxy -p ${ACCUMULO_HOME}/proxy/proxy.properties
5.Generated the c# bindings, using the proxy.thrift IDL file
thrift --gen csharp $ACCUMULO_HOME/proxy/thrift/proxy.thrift
This resulted in the creation of a directory called gen-csharp under ${ACCUMULO_HOME}/proxy/thrift/
6. The files under gen-csharp are needed in the C# project, in section D, below.
7. Thrift.dll, is also needed.
D. C# project - Accumulo Client:
1. Created a project of type library.
2. Added the files under gen-csharp in step C5, above to the library
3. Added reference to thrift.dll
4. Built the library
E. Connecting to Accumulo from C#
In the C# project that reads/writes to Accumulo,
1. Added reference - thrift.dll
2. Added reference to the library built in section D, above
3. On the Accumulo server, started the proxy (refer step C4, above)
Here is some sample code, to read data, to try this functionality out..
using System;
using System.Text;
using System.Collections.Generic;
using Thrift.Protocol;
using Thrift.Transport;
namespace AccumuloIntegratorPrototype
{
class MainClass
{
static byte[] GetBytes(string str)
{
return Encoding.ASCII.GetBytes(str);
}
static string GetString(byte[] bytes)
{
return Encoding.ASCII.GetString(bytes);
}
public static void Main (string[] args)
{
try
{
String accumuloProxyServerIP = "xxx.xxx.x.xx";//IP
int accumuloProxyServerPort = 42424;//Port Number
TTransport transport = new TSocket(accumuloProxyServerIP, accumuloProxyServerPort);
transport = new TFramedTransport(transport);
TCompactProtocol protocol = new TCompactProtocol(transport);
transport.Open();
String principal = "root";//Application ID
Dictionary<string, string> passwd = new Dictionary<string,string>();
passwd.Add("password", "xxxxx");//Password
AccumuloProxy.Client client = new AccumuloProxy.Client(protocol);
byte[] loginToken = client.login(principal, passwd);//Login token
//{{
//Read a range of rows from Accumulo
var bScanner = new BatchScanOptions();
Range range = new Range();
range.Start = new Key();
range.Start.Row = GetBytes("d001");
//Need the \0 only if you need to get a single row back
//Otherwise, its not needed
range.Stop = new Key();
range.Stop.Row = GetBytes("d001\0");
bScanner.Ranges = new List<Range>();
bScanner.Ranges.Add(range);
String scanId = client.createBatchScanner(loginToken, "departments", bScanner);
var more = true;
while (more)
{
var scan = client.nextK(scanId, 10);
more = scan.More;
foreach (var entry in scan.Results)
{
Console.WriteLine("Row = " + GetString(entry.Key.Row));
Console.WriteLine("{0} {1}:{2} [{3}] {4} {5}", GetString(entry.Key.Row), GetString(entry.Key.ColFamily), GetString(entry.Key.ColQualifier), GetString(entry.Key.ColVisibility), GetString(entry.Value),(long)entry.Key.Timestamp);
}
}
client.closeScanner(scanId);
client.Dispose();
transport.Close();
}catch (Exception e)
{
Console.WriteLine(e);
}
//}}
}
}
}
Adding Thrift to C# project involves two steps:
Add the C# code that has been generated by means of the Thrift compiler
Build Thrift.DLL and add it as a reference to your project. Alternatively, it is possible to link the code into your project, however not recommended.
The C# code for step 1 is generated from a Thrift IDL file, which is typically part of the project. In your case, the IDL files are located under proxy/src/main/thrift in the Accumulo tree.
The Thrift compiler and library can be downloaded from http://thrift.apache.org. Note that some projects are using a older version of Apache Thrift, which is not necessarily the latest stable. As elserj mentioned in the comments, Accumulo 1.4.x depends on Thrift 0.6.1, Accumulo 1.5.x and greater depend on Thrift 0.9.0.

FileVersionInfo.GetVersionInfo() incorrect in Console Application

I'm getting some serious weirdness using FileVersionInfo.GetVersionInfo() and was hoping somebody might be able to help.
The basics of the issue is that I am iterating through all the files in a folder calling GetVersionInfo() on each. There are about 300 files. This works ok for all but 2 of the files. For these DLLs I am getting comepletely incorrect info back from GetVersionInfo().
In order to eliminate all other variables, I extracted this call into a simple test app and it still got the same problem. However, if I built the test app as a Windows Application (it was a Console Application initially) then the data came back correct.
Just to clarify, the incorrect data coming back when running as a Console App is not simply null info like you would get if the file didn't contain version data. It contained reasonable data, but just the wrong data. It's as if it's reading it from a different file. I've looked for a file that contains matching version data, but can't find one.
Why is this simple call functioning differently if built as a Console Application rather than a Windows Application?
If anyone can help with this I would be very grateful.
Rgds,
Andy
-- Code Added
using System;
using System.Diagnostics;
namespace test
{
class Program
{
static void Main(string[] args)
{
string file = "C:\\ProblemFile.dll";
FileVersionInfo version = FileVersionInfo.GetVersionInfo(file);
string fileName = version.FileName;
string fileVersion = version.FileVersion;
Console.WriteLine(string.Format("{0} : {1}", fileName, fileVersion));
}
}
}
This behaviour seems weird indeed. Could it be that the Console application does not load the DLL from the same place as the WinForms application does? This would mean that GetVersionInfo uses some other API than just Win32 CreateFile (maybe going through some DLL resolver mechanism, side-by-side or whatever); remember that under the covers, version.dll will be executing your request, not the CLR itself.
Looking at FileVersionInfo through Reflector points in another direction yet:
public static unsafe FileVersionInfo GetVersionInfo(string fileName)
{
// ...
int fileVersionInfoSize = UnsafeNativeMethods.GetFileVersionInfoSize(fileName, out num);
FileVersionInfo info = new FileVersionInfo(fileName);
if (fileVersionInfoSize != 0)
{
byte[] buffer = new byte[fileVersionInfoSize];
fixed (byte* numRef = buffer)
{
IntPtr handle = new IntPtr((void*) numRef);
if (!UnsafeNativeMethods.GetFileVersionInfo(fileName, 0, fileVersionInfoSize, new HandleRef(null, handle)))
{
return info;
}
int varEntry = GetVarEntry(handle);
if (!info.GetVersionInfoForCodePage(handle, ConvertTo8DigitHex(varEntry)))
{
int[] numArray = new int[] { 0x40904b0, 0x40904e4, 0x4090000 };
foreach (int num4 in numArray)
{
if ((num4 != varEntry) && info.GetVersionInfoForCodePage(handle, ConvertTo8DigitHex(num4)))
{
return info;
}
}
}
}
}
return info;
}
As you can see there, some interesting dance is going on with code pages. What if the DLLs you inspected had several version information resources attached to them? Depending on the culture of the program calling into GetVersionInfo, I guess that the code page related calls could return other results?
Take the time to check the resources of the DLLs and make sure that there is only one language/code page for the version information. It might point you at the solution, I hope.
Sure the "files" you're seeing aren't . and .. ? If you iterate through all files, you'll always see entries for . (current dir) and .. (up dir). GetVersion Info might well return anything for these. You'd have to filter these entries out manually by name.
File and Assembly versions are 2 different things.
Are you sure you are not expecting the other?
Update: Tried this. Didn't work.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace test
{
class Program
{
[DllImport("COMCTL32")]
private static extern int InitCommonControls(int nExitCode);
static void Main(string[] args)
{
InitCommonControls(0);
string file = "C:\\ProblemFile.dll";
FileVersionInfo version = FileVersionInfo.GetVersionInfo(file);
string fileName = version.FileName;
string fileVersion = version.FileVersion;
Console.WriteLine(string.Format("{0} : {1}", fileName, fileVersion));
}
}
}

Categories