[PROJ] Interface to PROJ from C#

Peter Townsend peter.townsend at maplarge.com
Thu Jan 5 08:50:56 PST 2023


I unfortunately can't just give away what I did, but I wrote my own C#
wrapper for proj.

I followed the instructions on the webpage over here:
https://proj.org/install.html#building-on-windows-with-vcpkg-and-visual-studio-2017-or-2019

If you didn't put vcpkg at c:\dev\vcpkg, just make a symlink and it should
work fine. Just be sure to run all related vcpkg install commands from the
original directory.

To build debug vs release, I use these commands in the x64 Native Tools
command prompt:
cmake -DBUILD_SHARED_LIBS=ON
-DCMAKE_TOOLCHAIN_FILE=C:\dev\vcpkg\scripts\buildsystems\vcpkg.cmake ..
cmake --build . --config Debug -j 8

cmake -DBUILD_SHARED_LIBS=ON
-DCMAKE_TOOLCHAIN_FILE=C:\dev\vcpkg\scripts\buildsystems\vcpkg.cmake ..
cmake --build . --config Release -j 8

Beyond that, I forked the regular proj repo to add/modify a few things, and
I have a C# wrapper DLL that handles the P/Invoke calls. Additionally, we
generate a pair of internal Nuget packages. One contains the dll references
only. It'll put the following DLLs in runtimes/win-x64/native:
jpeg62.dll, libcurl.dll, "proj dll output from the build", "my custom C
proj dll", lzma.dll, sqlite3.dll, tiff.dll, and zlib1.dll.

Similarly we've got a linux equivalent that does runtimes/linux-x64 and
linux-arm64.

The second nuget package is the proj.db and friends that appear in the data
folder after building proj: alaska, BETA2007.gsb, conus, egm96_15.gtx,
GL27, ITRF2000, MD, nad27, nad83, ntf_r93.gsb, ntv1_can.dat, ntv2_0.gsb,
proj.db, and proj.ini. When I create a proj context object, I make sure to
give it their path via SetSearchPaths.

You don't necessarily need nuget packages if you do a post build copy step
or something along those lines.

In my custom C++ code I made a whole set of methods to ensure that all
strings are CoTaskMemAlloc'd so they happily marshal to the .NET world. It
was a bit of a pain to do, but I don't get random crashes dealing with
strings anymore. I also added methods for instantiating certain unmanaged
objects so I can get at their memory address.

In the C# wrapper, I set up a system like this. I have ProjObjects and
ProjHandles whose main job is to make sure everything gets disposed
properly. The only hitch is dealing with the default ProjContext and making
sure not to ever kill that.

public abstract class ProjObject : IDisposable {

private bool isDisposed;
public bool IsDisposed { get => isDisposed; }

public void Dispose() {
if (this.isDisposed)
return;

this.DisposeCore();
this.isDisposed = true;
}
protected virtual void DisposeCore() { }
}

public abstract class ProjHandleObject<T> : ProjObject,
IProjHandleObject<T> where T : ProjSafeHandle {

protected readonly T handle;
internal T Handle => handle;
T IProjHandleObject<T>.Handle => handle;

public IntPtr UnsafeAddress => Handle.DangerousGetHandle();

public virtual bool IsValid => !handle.IsInvalid;

internal ProjHandleObject(T handle) => this.handle = handle ?? throw new
ArgumentNullException(nameof(handle));

protected override void DisposeCore() {
this.handle.Dispose();

base.DisposeCore();
}

}

public interface IProjObject : IDisposable {

bool IsDisposed { get; }
bool IsValid { get; }

}
internal interface IProjHandleObject<T> : IProjObject where T :
ProjSafeHandle {


T Handle { get; }

}

public abstract class ProjSafeHandle : SafeHandle {

public override bool IsInvalid => handle.ToInt64() == 0;

protected ProjSafeHandle() : base(IntPtr.Zero, true) { }
protected ProjSafeHandle(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) {
}

}

Here's an actual sample object w/ handle:
public class ProjTypesArray : ProjHandleObject<ProjTypesArrayHandle> {

private readonly int length;
public int Length => length;

[System.Diagnostics.DebuggerStepThrough]
internal ProjTypesArray(ProjTypesArrayHandle handle, int length) :
base(handle) {
this.length = length;
}

}

public class ProjTypesArrayHandle : ProjSafeHandle {

[System.Diagnostics.DebuggerStepThrough]
internal ProjTypesArrayHandle() { }

protected override bool ReleaseHandle() {
//No other verification other than it doesn't die.
Api.ProjProxy.Iso.CrsList.projproxy_destroy_unmanaged_pj_types_array(this.handle);
return true;
}

}

PInvoke call looks like this:
[DllImport(PROJ_PROXY_DLL, EntryPoint =
"projproxy_create_unmanaged_pj_types_array", CallingConvention =
CallingConvention.Cdecl)]
public extern static ProjTypesArrayHandle
projproxy_create_unmanaged_pj_types_array(PJ_TYPE[] types, long typesCount);

On Thu, Jan 5, 2023 at 6:31 AM Bert Huijben <bert at qqmail.nl> wrote:

> When I tried to use proj from C# a few years back I created a wrapper
> library SharpProj (https://github.com/AmpScm/SharpProj.git)
>
>
>
> All sourcecode and buildscripts that work for me are in the repository,
> but I’m guessing you can just use NuGet and use the latest proj directly
> with some of the sample code from there.
>
>
>
>
>
> The easiest way to build proj yourself on Windows is current most likely
> using vcpkg. That helps building proj and all its dependencies.
>
>
>
>                Bert
>
>
>
> *From:* PROJ <proj-bounces at lists.osgeo.org> *On Behalf Of *Jonathan
> Johansen
> *Sent:* Thursday, January 5, 2023 4:08 AM
> *To:* proj at lists.osgeo.org
> *Subject:* [PROJ] Interface to PROJ from C#
>
>
>
> Hi all!
>
>
>
> I'm trying to use PROJ 9.1.1 in my C# Windows project, but am not having
> any success. A friend has tried to use it from EasyGIS, but ran into an
> issue. So I thought I'd try SharpProj (https://github.com/AmpScm/SharpProj),
> but I ran into https://github.com/AmpScm/SharpProj/issues/25. So I
> thought why not try to use the win DLL directly myself? So I got the 64
> bit OSGeo4W from https://proj.org/install.html#windows, and grabbed all
> of the DLLs in the bin folder, and have put them into my project. But it
> throws a similar error:
>
> >'Unable to load DLL 'proj_9_1.dll' or one of its dependencies: The
> specified module could not be found.
>
> So I thought I'd try to build the DLLS myself and use them. So I've got
> the 9.1.1 source code, installed cmake, started going through the first
> tutorial, and am trying. I'm not familiar with Linux, and I'm building on
> Windows (I see one entry in the mailing list from June 2022 about windows),
> so this is probably not going to be easy. Anyway, when trying to build it,
> I get this:
>
>
>
> >D:\Downloads\proj_build>cmake ../proj-9.1.1
> -- Selecting Windows SDK version 10.0.19041.0 to target Windows 10.0.19045.
> -- Requiring C++11
> -- Requiring C++11 - done
> -- Requiring C99
> -- Requiring C99 - done
> -- Configuring PROJ:
> -- PROJ_VERSION                   = 9.1.1
> -- PROJ_ABI_VERSION               = 9_1
> CMake Warning at cmake/ProjConfig.cmake:48 (message):
>   Autoconf's D:/Downloads/proj-9.1.1/src/proj_config.h may interfere with
>   this CMake build.  Run 'make distclean' in the source directory before
>   CMake's build.
> Call Stack (most recent call first):
>   CMakeLists.txt:127 (include)
>
> -- nlohmann/json: internal
> CMake Error at CMakeLists.txt:176 (message):
>   sqlite3 binary not found!
>
> CMake Error at CMakeLists.txt:181 (message):
>   sqlite3 dependency not found!
>
> CMake Error at CMakeLists.txt:187 (message):
>   sqlite3 >= 3.11 required!
>
> CMake Error at C:/Program
> Files/CMake/share/cmake-3.25/Modules/FindPackageHandleStandardArgs.cmake:230
> (message):
>   Could NOT find TIFF (missing: TIFF_LIBRARY TIFF_INCLUDE_DIR)
> Call Stack (most recent call first):
>   C:/Program
> Files/CMake/share/cmake-3.25/Modules/FindPackageHandleStandardArgs.cmake:600
> (_FPHSA_FAILURE_MESSAGE)
>   C:/Program Files/CMake/share/cmake-3.25/Modules/FindTIFF.cmake:124
> (FIND_PACKAGE_HANDLE_STANDARD_ARGS)
>   CMakeLists.txt:198 (find_package)
>
> -- Configuring incomplete, errors occurred!
> See also "D:/Downloads/proj-9.1.1/CMakeFiles/CMakeOutput.log".
>
>
>
> I have copied the sqlite3.exe and .dll from OSGeo4W into both folders, and
> it still doesn't think sqlite is there. I'll keep going through cmake
> tutorials to try to understand how I can satisfy it, but any help is
> appreciated - even better if SharpProj can be made to work. At this point I
> am a bit sad as I'm in over my head. I can supply a basic project trying to
> use the OSGeo4W DLLs too, if you'd like to look at that.
>
>
>
> Thanks so much for reading to this point!
>
>
>
> *Jonathan Johansen*
>
> M: 0438 922 553 <0438922553>
>
> E: jonathan at t3rra.com
> W: http://www.t3rra.com/
>
>
> _______________________________________________
> PROJ mailing list
> PROJ at lists.osgeo.org
> https://lists.osgeo.org/mailman/listinfo/proj
>


-- 
Peter Townsend
Senior Software Developer
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osgeo.org/pipermail/proj/attachments/20230105/21a16d9e/attachment.htm>


More information about the PROJ mailing list