locationtech / proj4j

Java port of the Proj.4 library for coordinate reprojection
Other
186 stars 73 forks source link

Unknown projection: longlat #111

Open Tom3652 opened 5 months ago

Tom3652 commented 5 months ago

Thanks for the package !

When i try to transform EPSG:2154 i am getting this error : Unkown projection: longlat

From this method :

 private Projection parseProjection(Map params, Ellipsoid ellipsoid) {
        Projection projection = null;

        String s;
        s = (String) params.get(Proj4Keyword.proj);
        if (s != null) {
            projection = registry.getProjection(s);
            if (projection == null)
                throw new InvalidValueException("Unknown projection: " + s); // Here
        }
        else {
            throw new InvalidValueException("Keyword '" + Proj4Keyword.proj + "' is a required parameter");
        }
        projection.setEllipsoid(ellipsoid);
      ... 
 }

I am using this method to call the transform :

 public ProjCoordinate transform(double lon, double lat) {
        try {
            CRSFactory factory = new CRSFactory();
            CoordinateReferenceSystem srcCrs = factory.createFromName("EPSG:4326");
            CoordinateReferenceSystem dstCrs = factory.createFromName("EPSG:2154");
            BasicCoordinateTransform transform = new BasicCoordinateTransform(srcCrs, dstCrs);
            ProjCoordinate srcCoord = new ProjCoordinate(lon, lat);
            ProjCoordinate dstCoord = new ProjCoordinate();
            transform.transform(srcCoord, dstCoord);
            Log.d(TAG, "Dst Coordinates : " + dstCoord.toShortString());
            return dstCoord;
        } catch (Exception e) {
            onError("Error transforming lat " + lat + " - lng " + lon + " : " + e.getMessage());
        }
        return null;
    }

The entire error message i have is : Error transforming lat 43.094308831666566 - Ing 5.825118781666667 Unknown projection: longlat So i have checked that my lat - lng were correct and it actually works fine sometimes, sometimes i have the error.

Can i know more why i have this error and how can i fix it please ?

pomadchin commented 5 months ago

hi @Tom3652, how is proj4j added into the project? also what version is the project dependent on?

Tom3652 commented 5 months ago

Hello @pomadchin ! Thanks for your reply, it is added in the app/build.gradle file this way :

implementation 'org.locationtech.proj4j:proj4j:1.3.0'
implementation 'org.locationtech.proj4j:proj4j-epsg:1.3.0'

I have also tried an older version that doesn't use the separated package proj4j-epsg : implementation 'org.locationtech.proj4j:proj4j:1.1.5' but i am getting the same error. I have an app released on the store with version implementation 'org.locationtech.proj4j:proj4j:1.1.3' working fine.

Don't hesitate if you need more information !

pomadchin commented 5 months ago

🤔 hmm, that indeed does look like the packages separation affected somehow the build. Unfortunately the projects im using proj4j in did not suffer anything (https://github.com/locationtech/geotrellis/blob/master/project/Dependencies.scala#L77-L78)

Could you check the dep tree to ensure the proj4j-epsg resources folder is properly pulled?

pomadchin commented 5 months ago

The only idea I can come up is that no projection files in the classapatch are causing this error

Tom3652 commented 5 months ago

Thanks for pointing it out, it's not code relevant then, only configuration somehow.

Yes well the files seem to be here :

+--- org.locationtech.proj4j:proj4j:1.3.0
+--- org.locationtech.proj4j:proj4j-epsg:1.3.0

However is it possible that it has something to do with release / debug mode ? The error seems to only occur when i build an APK and install it on my device. If i run through Android Studio it works fine with no errors.

I have a proguard-rules.pro file as well, is it possible it has an impact on it ?

pomadchin commented 5 months ago

If it works in the studio but not in a shape of a package => that's definitely smth related to packaging.

// I haven't dealt with Androd for quite some time

Tom3652 commented 5 months ago

Thanks for your reply, at least i know this something related to this, i will try to figure it out and post a solution here when i found one.

pomadchin commented 5 months ago

Great! Feel free to drop here anything; I'll try to help u as much as I can!

Tom3652 commented 5 months ago

Thank you very much, really apreciated ! I am planning to call few methods from the proj4j-epsg package with fake params (and display the output) just to make sure the package is imported in the released bundled app 🙏🏻

pomadchin commented 5 months ago

@Tom3652 Btw, proj4j-epsg is just a bunch of files from resources

Tom3652 commented 5 months ago

Oh i see thank you ! i see the structure of the /nad/epsg file, i am only planning to use EPSG 2154 : <2154> +proj=lcc +lat_1=49 +lat_2=44 +lat_0=46.5 +lon_0=3 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs <>

Will dig deeper

Tom3652 commented 5 months ago

Well i see that there are plenty of longlat inside the proj field :

Capture d’écran 2024-04-18 à 16 16 06

Is there any chance that when i call CoordinateReferenceSystem dstCrs = factory.createFromName("EPSG:2154"); it maps towards a bad Projection ?

Because if i have this error, in my opinion it means it actually reads the file but parse the "wrong" +proj field no ?

-> I am telling this because i just got the issue in debug mode... So it's not related to release only but doesn't happen every single time in debug either :/

pomadchin commented 5 months ago

Looking closer now.

Unknown projection happens only here meaning it has not latlon projection in the registry.

Registry is declared over there

and it is purely in mem

Wondering what happens to the project init on your app side. Definitely need to be a reproducible example to assist further :/

Tom3652 commented 5 months ago

Thank you very much, i had not followed the in mem insight you gave me but indeed there is defintely no "reason" it happens

Will run more tests and see if i can debug it further, until forking the project and add some debug logs if i have to 😅

pomadchin commented 5 months ago

Oh, in mem is that this registry is just a HashMap filled in in the object constructor.

Wondering how the debug mode may affect it 🤔

Tom3652 commented 5 months ago

Alright i am getting closer, i have logged this :

public ProjCoordinate transform(double lon, double lat) {
        CoordinateReferenceSystem srcCrs = null;
        CoordinateReferenceSystem dstCrs = null;
        try {
            CRSFactory factory = new CRSFactory();
            List<Projection> projections = factory.getRegistry().getProjections();
            if (projections.isEmpty()) {
                onError("No projections found in registry !");
            }
            for (int i = 0; i < projections.size(); i++) {
                Projection projection = projections.get(i);
                onError("Getting projection code : " + projection.getEPSGCode() + " with name : " + projection.getName());
            }
            srcCrs = factory.createFromName("EPSG:4326");
            dstCrs = factory.createFromName("EPSG:2154");
        } catch (Exception e) {
            Log.d(TAG, "Error transforming lat - lng : " + e);
            // onError("Error creating coordinates lat " + lat + " - lng " + lon + " : " + e.getMessage());
        }
}

And i am getting No projections found in registry !, so i need to figure out why there are no projections in the registry 😂 (the registry is not null though) !

Tom3652 commented 5 months ago

It really makes me wonder if the commented lines at the begining of this method are actually very useful in release mode :

public Projection getProjection(String name) {
        // if ( projRegistry == null )
        // initialize();
        Class cls = (Class) projRegistry.get(name);
        if (cls != null) {
            try {
                Projection projection = (Projection) cls.newInstance();
                if (projection != null)
                    projection.setName(name);
                return projection;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                System.err.println("Cannot instantiate projection " + name + " [" + cls.getName() + "]");
                e.printStackTrace();
            }
        }
        return null;
    }

Because it seems there is an issue with projRegistry in release mode :/

// if ( projRegistry == null )
        // initialize();
Tom3652 commented 5 months ago

Hello @pomadchin , do you think it might be a bug now ? Knowing that this is the registry that is empty, i am not sure i can do much, the packaging seems fine, this is more looking like some race condition

Do you think this might be happening because of the register method ? the weird part is that it's like the register method is never called in release mode, the "only" guess point i can make at this moment is that projRegistry != null but projRegistry is empty :

Capture d’écran 2024-04-22 à 17 23 25

pomadchin commented 5 months ago

@Tom3652 if that's possible to generate a reproducible example that could be some bug that just got revealed by resources in different packages :+1:

Tom3652 commented 5 months ago

@pomadchin i can't reproduce it for now but i know a real factor that causes the issue :

When i set the Kotlin version of my app to ext.kotlin_version = '1.8.0' and my gradle to : classpath 'com.android.tools.build:gradle:7.4.2' It works fine.

When i set them to the latest version : ext.kotlin_version = '1.9.23' and my gradle to : classpath 'com.android.tools.build:gradle:8.3.2'

Using Gradle 8.4 distribution instead of 7.5.1, it simply doesn't work.

So the conclusion is that the newest version of Gradle / Kotlin compiling files is changing maybe the way HashMap behaves or how the Java is run, i have no idea unfortunately i am a Flutter developer 😅

I will try to make reproductible example when i have the time, but for now my project is saved by rolling back to older version of Kotlin and Gradle ! 🙏🏼

pomadchin commented 5 months ago

That's interesting, I'd be curious to see diff in the bytecode ha.

dprks commented 3 months ago

Hi friends! I suspect that the issue is to do with the addition of R8, replacing Proguard for code optimisation. R8 appears to be more aggressive than Proguard, and perhaps it is for this reason that your projection classes are missing at runtime.

I'm far from an expert on the matter, but hopefully you'll have similarly good luck by adding the following incantation to your proguard-rules.pro file: -keep class org.locationtech.proj4j.proj.** { *; }

This should keep all projection classes from being optimised into oblivion. If know at compile time which projections you'll need and you wish to save a few bytes, you can probably instead choose to only keep the projections you'll need. e.g. -keep class org.locationtech.proj4j.proj.LongLatProjection { *; }

I hope this helps!

Tom3652 commented 1 month ago

hey @dprks !

It seems to be working with the pro-guard rule, thank you very much !