Conflict between Proj and GeoStats in a package

I am new to creating packages and usually just run scripts.

This script runs fine:

using CSV
using DataFrames
using Dates
using GeoIO
using GeoStats
using Proj
using ZipArchives
include("mweNLHFmethod.jl")

#using Revise
#using UpdateTerminatedPostcodes

println(UTPfindNewPostcodes(["BT4 3EU", "OX17 2JP", "NW1 5HA", "BT9 5LA"]))

and prints (149890.41568462105, 529224.907298712).

However, if I make the include file into a package, and swap the commented using sections above, it errors.

module UpdateTerminatedPostcodes

# imported dependencies
using CSV
using DataFrames
using Dates
using GeoIO
using GeoStats
using Proj
using ZipArchives

# files defining functions, types, etc.
include("mweNLHFmethod.jl")

# names you want to make public
export UTPfindNewPostcodes

end # module UpdateTerminatedPostcodes

This is the include file (reduced to MWE)

function UTPfindNewPostcodes(postcode_list)
    INGtoBNG = Proj.Transformation("EPSG:29903", "EPSG:27700", always_xy=true) # EPSG:29903 is Irish National Grid
    BNGtoING = Proj.Transformation("EPSG:27700", "EPSG:29903", always_xy=true) # EPSG:27700 is British National Grid
    out = INGtoBNG(337492.0, 374065.0) # Convert Irish national grid coordinates to British national grid
    return out
end

Now when I run, I get:

Precompiling UpdateTerminatedPostcodes...
  1 dependency successfully precompiled in 29 seconds. 440 already precompiled.
ERROR: LoadError: UndefVarError: `Proj` not defined in `UpdateTerminatedPostcodes`
Hint: It looks like two or more modules export different bindings with this name, resulting in ambiguity. Try explicitly importing it from a particular module, or qualifying the name with the module it should come from.
Hint: a global variable of this name may be made accessible by importing Meshes in the current active module Main
Hint: a global variable of this name may be made accessible by importing GeoStats in the current active module Main
Hint: Proj is loaded but not imported in the active module Main.
Stacktrace:
 [1] UTPfindNewPostcodes(postcode_list::Vector{String})
   @ UpdateTerminatedPostcodes C:\Users\TGebbels\...\Documents\Julia\Packages\UpdateTerminatedPostcodes\src\mweNLHFmethod.jl:2
 [2] top-level scope
   @ c:\Users\TGebbels\...\Documents\Julia\Julia Experimenting\From NLPU Shared\mwe-Proj - Nov24.jl:18
 [3] include(fname::String)
   @ Main .\sysimg.jl:38
 [4] run(debug_session::VSCodeDebugger.DebugAdapter.DebugSession, error_handler::VSCodeDebugger.var"#3#4"{String})
   @ VSCodeDebugger.DebugAdapter c:\Users\TGebbels\.vscode\extensions\julialang.language-julia-1.127.2\scripts\packages\DebugAdapter\src\packagedef.jl:122
 [5] startdebugger()
   @ VSCodeDebugger c:\Users\TGebbels\.vscode\extensions\julialang.language-julia-1.127.2\scripts\packages\VSCodeDebugger\src\VSCodeDebugger.jl:45
 [6] top-level scope
   @ c:\Users\TGebbels\.vscode\extensions\julialang.language-julia-1.127.2\scripts\debugger\run_debugger.jl:12
 [7] include(mod::Module, _path::String)
   @ Base .\Base.jl:557
 [8] exec_options(opts::Base.JLOptions)
   @ Base .\client.jl:323
 [9] _start()
   @ Base .\client.jl:531
in expression starting at c:\Users\TGebbels\...\Documents\Julia\Julia Experimenting\From NLPU Shared\mwe-Proj - Nov24.jl:18

I’m afraid I can’t figure out what the hints are trying to tell me. Can someone give me a stronger hint, please?
Thanks,
Tim

Hi @TimG , the hint above has the information you need.

You are loading the Proj module with using Proj and also loading the Proj transform from GeoStats.jl when you do using GeoStats. You need to specify which of the two you want to use with a prefix (e.g., GeoStats.Proj).

If I try

    INGtoBNG = GeoStats.Proj.Transformation("EPSG:29903", "EPSG:27700", always_xy=true) # EPSG:29903 is Irish National Grid

and drop using Proj, I get

ERROR: LoadError: type UnionAll has no field Transformation

The GeoStats.Proj is the one documented in our stack:

  Proj(CRS)
  Proj(code)

  Convert the coordinates of geometry or domain to a given coordinate reference
  system CRS or EPSG/ESRI code.

  Optionally, the transform samples the boundary of polytopes, if this option is
  true, to handle distortions that occur in manifold conversions.

  Examples
  ========

  Proj(Polar)
  Proj(WebMercator)
  Proj(Mercator{WGS84Latest})
  Proj(EPSG{3395})
  Proj(ESRI{54017})

  Notes
  –––––

    •  By default, only the vertices of the polytopes are transformed,
       disregarding distortions that occur in manifold conversions. To handle
       this case, use TransformedGeometry.

You are trying to use the Proj.jl (package) interface in our GeoStats.Proj object.

You need to decide which of the two you want to use, and then use the appropriate interface.

I know which one I want to use (Proj.jl), but I don’t know how to invoke it!

It works straightforwardly when I just include the file in a script, but it doesn’t work in a package. I don’t know why.

The hint says

Hint: It looks like two or more modules export different bindings with this name, 
resulting in ambiguity. Try explicitly importing it from a particular module, or 
qualifying the name with the module it should come from.

I was using Proj so I switched to import Proj and now it works.

module UpdateTerminatedPostcodes

# imported dependencies
using CSV
using DataFrames
using Dates
using GeoIO
using GeoStats
using ZipArchives

import Proj

# files defining functions, types, etc.
include("mweNLHFmethod.jl")

# names you want to make public
export UTPfindNewPostcodes

end # module UpdateTerminatedPostcodes

I guess I don’t understand the difference between import and using.

Tim

The Julia manual should have a good section on import vs using, and on solving name conflicts.

I have read the manual (multiple times) but I’m still confused because:

  • I did use a fully qualified name (Proj.Transformation) in my code
  • I am not (knowingly) trying to extend an existing Proj method.

I’m also not at all clear why using works in script while I have to use import when I put the same code in a module.

There must be something else (probably many things!) I didn’t understand on my reads through.

(Note to self: Keep studying!)

The line using GeoStats exports the name Proj. The line using Proj makes the name Proj available as a module. The line import Proj doesnt export the names of the module and apparently also overwrites any existing Proj name.

Given that youre mainly using GeoStats.jl I recommend that you keep using GeoStats and import the Proj.jl package with other name:

import Proj as CProj

That will eliminate any possible conflict.