maplibre / maplibre-native

MapLibre Native - Interactive vector tile maps for iOS, Android and other platforms.
https://maplibre.org
BSD 2-Clause "Simplified" License
1.09k stars 319 forks source link

MapLibre Native Compose Multiplatform Library #2638

Closed louwers closed 9 hours ago

louwers commented 4 months ago

StreetComplete is looking to implement an iOS version of the app. The largest blocker is that there is no Compose Multiplatform library that uses MapLibre Native. If you are also interested in this, please reach out to @westnordost, maybe some kind of collaboration to work on this is possible.

Jetbrains has a template available here: https://github.com/KevinnZou/compose-multiplatform-library-template/tree/main

An example Compose Multiplatform library is WebView for JetBrains Compose Multiplatform

It uses Native C Interop: https://kotlinlang.org/docs/native-c-interop.html#simple-example with cinterop.

Related PR: https://github.com/maplibre/maplibre-native/pull/1254

tarkvara commented 4 months ago

For the record, we were able to get MapLibre working in our KMP app (both Android and iOS). It was however a home-grown solution, and there were some rough edges.

louwers commented 4 months ago

@tarkvara Can you share more details?

tarkvara commented 4 months ago

I wasn't ambitious enough to create a KMP library, so Android and iOS directly refer to the native libraries. Perhaps somebody with a better understanding of the KMP build tools can wrap them together into a single KMP library.

For Android, it was enough to add maplibre-android = { group = "org.maplibre.gl", name = "android-sdk", version = "11.0.1" } and the corresponding dependency under androidMain.dependencies.

For iOS, I added

        with (iosTarget.compilations) {
            getByName("main") {
                cinterops {
                    create("MapLibre") {
                        packageName("org.maplibre")
                        compilerOpts(
                            "-framework",
                            "MapboxLibre",
                            "-Fsrc/iosFrameworks/MapLibre.xcframework/$subDir"
                        )
                    }
                }
            }
        }
        iosTarget.binaries.all {
            // Tell the linker where the framework is located.
            linkerOpts(
                "-framework",
                "MapLibre",
                "-F../composeApp/src/iosFrameworks/MapLibre.xcframework/$subDir"
            )
        }

Which unfortunately isn't quite correct; the Gradle build doesn't correctly link, so I had to build the app from within Xcode.

As I said, it seems to work for our app, but I wouldn't recommend it as a general solution.

amirhammad commented 4 months ago

Hi! I just posted this today this in Slack and then found this issue.

Hello! I'm just wondering if there is anybody already working on maplibre in compose multiplatform. I think it is the missing piece in compose multiplatform app development - maps. I have already started creating a compose multiplatform library that uses maplibre native(ios and android) under the hood. I reworked the expressions to kotlin and the first tests look promising: loading styles, updating camera, adding layers and sources programatically. But maybe before going any further I just wanted to check if there is already a project doing this exact same thing. Is there? Thanks :slightly_smiling_face:

I was thinking of creating a proper opensource library for it, but I wanted to make sure it didn't exist yet. But seeing this issue, gives me enough motivation to continue.

westnordost commented 4 months ago

Thank you for posting this ticket, @louwers !

@amirhammad Wow, that's cool! There exists in fact a Compose library using MapLibre, Ramani-Maps. But it is for Jetpack Compose and Android-only. Additionally, I am not sure if the API of this library has been designed to support the whole feature set of MapLibre in mind. (see https://github.com/ramani-maps/ramani-maps/issues/77 which I just opened) At the very least, it can serve as a base or inspiration how the API could look like and how to wire together MapLibre with Compose in general. (But I just realized that I didn't read your cited message to the end - you already did that)

I am also highly interested in this, though I just started using Compose a few months ago, so I don't feel confident to help implementing a Compose library and design its API just yet. In terms of designing a perfect compose API, it might also be worth looking at and learning from the Google's Maps Compose library.

JonasVautherin commented 4 months ago

Hej! Co-author of Ramani-Maps here. We would love it if Ramani-Maps worked with Compose Multiplatform! Back when we started, I quickly tried to get Maplibre to run on Compose Multiplatform on my Linux system ("natively", i.e. not with a WebView and Javascript) and my conclusion was that it would take some work there.

If you manage to make Ramani-Maps work "natively" on iOS or Desktop, I would be willing to have a look and help bring that upstream. I am however not very open to solving it with a WebView and Javascript :see_no_evil:.

Additionally, I am not sure if the API of this library has been designed to support the whole feature set of MapLibre in mind.

Completely open to contributions! And of course if you feel like you will be better off on your own fork, feel free (it's MPLv2-licensed).

ianthetechie commented 4 months ago

Looping in @Archdoog here. We currently have a fork of Ramani that's a bit more active (would love to upstream but it's been a bit tough getting responses, and Ramani is currently a good bit broader with Mapbox support, drawing, and a lot of things besides just a core composable library).

We are not presently targeting compose multi platform but aren't opposed to it :)

Does anyone on the thread know how different or related Jetpack Compose and Compose Multiplatform are? EDIT: It looks like the JetBrains README has a helpful excerpt. The devil lies in the details, but this looks promising:

Compose Multiplatform shares most of its API with Jetpack Compose, the Android UI framework developed by Google. You can use the same APIs to build user interfaces for both Android and iOS.

Assuming they are similar, I think it would be a shame to have too many diverging approaches, since we are all going to run into most of the same challenges. Jacob and I have put a lot of work into getting camera stuff correct, as this is a HUGE pain in every similar UI framework (same on iOS, where we are developing a similar wrapper for SwiftUI).

JonasVautherin commented 4 months ago

would love to upstream but it's been a bit tough getting responses

@ianthetechie: have I missed questions somewhere? :confused:

Also note that we have dropped Mapbox support (because Mapbox doesn't play nice with us).

Archdoog commented 4 months ago

Hey @JonasVautherin! I think @ianthetechie had some initial chats with you on the maplibre slack channel. We tried reaching out there a handful of times.

As for a path forward, I'd suggest we set up a call to give you an introduction to where we've gone from forking ramani to where https://github.com/Rallista/maplibre-compose-playground sits now. There are a few key differences that we could chat about.

louwers commented 4 months ago

You are always welcome to put this on the agenda for the MapLibre Native TSC Meeting as well.

JonasVautherin commented 4 months ago

We tried reaching out there a handful of times.

I see. I have not been active on Slack there for a while indeed. The best way to upstream changes is probably to open an issue/PR on the git repository (i.e. the MapLibre Slack is not the official way to reach Ramani-Maps).

We can definitely have a call, I would be happy to hear your thoughts. However I probably won't bring your fork upstream myself. I am happy to discuss, review and merge contributions (on a best-effort basis), but someone would have to make those contributions :blush:. If that isn't possible, then as I said you are welcome to keep working on your fork (as long as you honour the licence) :+1:.

This said, I wouldn't say Ramani is not active. I believe I have been pretty responsive regarding issues and PRs on the repo. To be honest, I find it a bit unfair to say it has been "tough getting responses".

westnordost commented 3 months ago

~Does anyone on the thread know how different or related Jetpack Compose and Compose Multiplatform are?~

Compose Multiplatform is a soft fork of Jetpack Compose, for example, the package names are the same. So theoretically, one could switch out the dependency and be ready for multiplatform. Furthermore, some Jetpack compose APIs may not be available on multiplatform, i.e. they cannot be used or need to have an implementation for every platform. For example stringResource(stringResId: Long), painterResource(drawableResId: Long) etc. are only available on the Android platform (as they access Android resources).

Otherwise, so that the library itself is multiplatform and not only uses a multiplatform library as a dependency, it itself needs to be set up like one, i.e. with the directly structure like src/commonMain, src/androidMain etc. and the correct gradle configuration.

I recently migrated a smallish Java library to a Kotlin Multiplatform library (nothing with Compose though). Maybe this can help:

Jacob and I have put a lot of work into getting camera stuff correct, as this is a HUGE pain in every similar UI framework (same on iOS, where we are developing a similar wrapper for SwiftUI).

Interesting, I would think that the easiest approach would be to ... uh ... basically don't worry about camera animations at all but leave that up to Compose. I.e. just expose the camera position on the API and users would just use the usual Compose stuff for animations. Or is even that a PITA? (I am somewhat of a newbie with Compose, so I only have a very rough idea how that would look like.)

westnordost commented 3 months ago

Anyway, there is talk about a call - I'd like to join too. Did someone mention a date or a doodle for date-finding? If not, @ianthetechie would you mind setting this up? You two seem to be the ones that advanced the most from what I read here.

ydrea commented 3 months ago

+1 on the call initiative!

SebastianAigner commented 3 months ago

Hi! Seb from JetBrains / Compose Multiplatform here. Would be awesome to have MapLibre available for Compose Multiplatform apps on iOS and Android (or even beyond!) – Let us know if there is something we can help with or if there's any questions that come up during your discussions – happy to connect!

ianthetechie commented 3 months ago

I've created a Doodle here so we can all get on a call for an hour to discuss :) https://doodle.com/meeting/participate/id/b44RqJ7b

See you soon!

boldtrn commented 3 months ago

Thanks for organising this. I'd be interested in joining too, if we can find a good slot 😺

westnordost commented 2 months ago

By the way, there is another. When one searches for MapLibre Compose on DuckDuckGo, one will find this: https://github.com/dellisd/maplibre-compose

It's a bit older, but just yesterday, the guy added new commits.

So, to summarize, there are now:

dellisd commented 2 months ago

Thanks for the mention!

I don't have any concrete plans to continue working on maplibre-compose at the moment, but I may revisit it again in the future depending on the needs of my side projects.

Some other prior art I can throw into this thread which may be of some use:

michalgwo commented 1 month ago

There is also a library in progress by @skamirmaps, which he described on his blog https://amir.sk/32/compose-map-intro/

westnordost commented 1 month ago

Yes, that's the "ComposeMap" I mentioned above. But as long as the source is not available, TBH it's not really interesting because one cannot really talk about it on a technical level.

amirhammad commented 1 month ago

@westnordost, @michalgwo : Hello there! Sorry for replying a bit late. I'm still working on deploying the library to a maven repository.

Access

If anybody would like to try it out, I invite you just to just fill out the form and I will provide you with the library to play around with when it's ready(should be this weekend 🤞) You can find more info skamirmaps samples repository. There you can already see, although not be able to compile yet, how it can be used.

Open/Closed source

For now, I decided to not disclose the source code to general public, however I might reconsider later.

Contact

If anybody is interested to know more about technical details or discuss possible collaboration, please contact me at maps@amir.sk.

Thank you!

louwers commented 1 month ago

Hey @amirhammad thanks for giving an update on your progress! Proprietary software is out of scope of the MapLibre project and advertising/discussing proprietary software should happen on other channels. Of course if you run into any challenges while integrating MapLibre Native that would be in scope in a separate issue, we support all users/integrators of MapLibre Native here.

This discussion is intended to bring people together to discuss and develop an open source Compose Multiplatform library for MapLibre. Thanks for understanding.

sargunv commented 3 weeks ago

I'm working on a Kotlin Multiplatform app using MapLibre, and have a prototype of a Multiplatform wrapper for MapLibre Native. Currently my prototype supports:

Currently the code is under a package in my app (https://github.com/sargunv/train-tracker-app), but as it develops and I nail down patterns for a representative subset of features, I intend to split it off into its own library. Feel free to check out the code here: https://github.com/sargunv/train-tracker-app/tree/main/maplibre-compose

Some immediate TODOs I have are:

Once some API patterns are nailed down for those TODOs, it's mostly a matter of following those patterns to add support for all the various layer/source configuration (which I'd probably rely on PRs for, beyond my own use cases)

JonasVautherin commented 3 weeks ago

Nice work @sargunv!

Out of curiosity: do I understand correctly that this is a wrapper around MapLibre-iOS (what I see here) and MapLibre-Android (see here)?

I guess I am a little confused because you mention that it is a "Multiplatform wrapper for MapLibre Native". But it feels like it is wrapping the Android/iOS SDKs, not the MapLibre Native (the C++ library), right? Or am I missing something?

sargunv commented 3 weeks ago

Ah yup, it's a multiplatform wrapper around the android/iOS sdks (which I thought are part of the MapLibre Native project but I might have my terminology mixed up)

Not fundamentally opposed to interfacing with the actual native library itself; I just chose the route that felt easier to get off the ground

louwers commented 3 weeks ago

They are both part of the MapLibre Native project! So "Multiplatform wrapper for MapLibre Native" is correct.

Not fundamentally opposed to interfacing with the actual native library itself; I just chose the route that felt easier to get off the ground.

Definitely true. Especially since we don't have a C FFI yet (which seems the way to go if you want to interface with native libraries directly with Kotlin Multiplatform). I think this will be developed over the course of the next year, since it is needed for getting the most performant implementation for Flutter possible, something Toyota has indicated interest in.

But for now building on the Android and iOS SDKs seems to be the way to go for sure. They are widely in use and are not going anywhere.

JonasVautherin commented 3 weeks ago

They are both part of the MapLibre Native project! So "Multiplatform wrapper for MapLibre Native" is correct.

Right, my mistake. "Native" is a bit of a strange word: on Android, the "Native Development Kit" (C/C++) is in opposition to the "Software Development Kit" (Java/Kotlin). But then some people use "native" to mean it's using what you find in the official Android documentation (e.g. not a Javascript wrapper). It seems like "MapLibre-Native" is just the name of the project, which again is different.

Especially since we don't have a C FFI

Android can definitely interface with C++ over JNI, and I think iOS can as well through Objective-C++ :thinking:. C is probably better for interoperability in general: for instance I am not sure if Go can easily call C++. I am not sure I understand the performance issue with Flutter, though.

From where I stand, what prevents me from using the C++ library with KMP is not the API, but missing implementation. For instance the annotations are implemented in the Android module, not in C++ (if I understand correctly). Or am I missing something?

louwers commented 3 weeks ago

Android can definitely interface with C++ over JNI, and I think iOS can as well through Objective-C++ 🤔.

This is exactly what we are using for the Android and iOS SDKs. However, you could in theory use Kotlin Multiplatform's Interoperability with C as well. As you mention, you would need to re-implement some of the platform-dependent implementations (e.g. for making HTTP requests). Also the SDKs themselves contain additional functionality, although annotations do exist as a concept in the C++ Core as well.

JonasVautherin commented 3 weeks ago

you would need to re-implement some of the platform-dependent implementations (e.g. for making HTTP requests)

But I guess also the rendering engine, right? That felt like the complicated part to me :innocent:. But maybe that alone justifies having official wrappers for Android/iOS the way they are now, and going for a KMP approach like @sargunv is doing :+1:.

westnordost commented 3 weeks ago

FYI the current one and only HTTP library for KMP that works for both Android and iOS is Ktor Client. OkHttp is multiplatform, too, but AFAIK only for JVM and JS. And for parsing JSON (and thus GeoJson), the go-to solution is Kotlinx.serialization-json, which is also multiplatform for any target.

louwers commented 3 weeks ago

But I guess also the rendering engine, right?

@JonasVautherin No, the rendering engine is part of the C++ Core.

And for parsing JSON (and thus GeoJson), the go-to solution is Kotlinx.serialization-json, which is also multiplatform for any target.

@westnordost JSON parsing is is all implemented in C++. You wouldn't need to touch it.

FYI the current one and only HTTP library for KMP that works for both Android and iOS is Ktor Client.

You would need some kind of C++ implementation, because the C++ code makes the requests. For example platform/default has a HttpFileSource implementation that uses curl, iOS uses APIs part of Foundation, Android delegates the call to Kotlin land, etc.

Anyway, I don't want to derail the discussion. Just wanted to mention it as a possibility.

amirhammad commented 2 weeks ago

After a lot of consideration (maybe too much!) I realised skamirmaps should be open-source. https://github.com/skamirmaps/skamirmaps All outside contributions are welcome. Let's come up with APIs that are easy to use!

Thank you

sargunv commented 1 week ago

I just want to share that all the TODOs I mentioned in my previous comment are now implemented (except for the one about writing tests).

My experimental Multiplatform wrapper for the Maplibre SDKs now supports:

The code is available in the repo linked in my previous comment above.

Some more immediate TODOs before I feel ready to publish a v0.1 and open up to PRs:

Things I'd like to explore soon after v0.1:

sargunv commented 11 hours ago

https://github.com/sargunv/maplibre-compose

I’ve just published the first release to Maven! Docs are light at the moment but there’s source code for a demo app showcasing common use cases. Feedback and PRs welcome!