Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#255 LocalTile overlay #1876

Merged
merged 11 commits into from
Dec 14, 2017
Merged

Conversation

zavadpe
Copy link
Contributor

@zavadpe zavadpe commented Dec 13, 2017

Added support for locally stored tile overlay similar to UrlTile component. Usage example in CustomTilesLocal.js file, but basically one just adds tile overlay within the map:
<MapView ... >
<LocalTile
pathTemplate="/path/to/localy/stored/tiles/{z}/{x}/{y}.png"
tileSize={256}
/>
</MapView>
Path just points to the path where tiles are stored.

Added android native AirMapLocalTile and its respective
react component similar to UrlTile.
Local tile can load localy stored tiles as overlay similarly
as UrlTile.
Added ios sources to project

Added ios sources to project
@zavadpe zavadpe mentioned this pull request Dec 13, 2017
@rborn
Copy link
Collaborator

rborn commented Dec 13, 2017

@zavadpe could you please fix the issues travis found so we can review the PR? Thanks 😊

Copy link
Collaborator

@rborn rborn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, except the NSLog comment. Maybe @alvelig wants to have a look too 😊

@@ -62,6 +63,7 @@ @implementation AIRMap

- (instancetype)init
{
NSLog(@"AirMap.init................");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove all NSLog instances ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, this one somehow slipped my attention..

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zavadpe there are more :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, just found them :)

@alvelig
Copy link
Contributor

alvelig commented Dec 13, 2017

Apparently looks ok.

@rborn
Copy link
Collaborator

rborn commented Dec 13, 2017

@alvelig should I merge then this? (as you closed #1420 )

@alvelig
Copy link
Contributor

alvelig commented Dec 13, 2017

@zavadpe Great, but docs missing. Could you please add docs page. And mention instruments to download offline tiles. Thanks!

And also I have a question: is it compatible with online map? Do offline appear above the online tiles?

@rh389 we will, just minor changes are required.

@robhogan
Copy link
Contributor

Guess you meant @rborn ;)

@alvelig
Copy link
Contributor

alvelig commented Dec 13, 2017

@rh389 sorry... yes I meant @rborn LOL

@alvelig
Copy link
Contributor

alvelig commented Dec 13, 2017

@rborn let's merge. I created an issue for documentation. We can merge it later. I'm going to make a PR for documenting offline and custom tiles. Any help is welcome anyway.

@zavadpe
Copy link
Contributor Author

zavadpe commented Dec 14, 2017

@alvelig @rborn I'll update documentation in main README file under Using a custom Tile Overlay section. I can do it within this PR while it still isn't merged.

README.md Outdated
</MapView>
```

For Android: if original (for example Google) tiles are not desirable (no need to download them when using offline tiles), set mapType to 'none'.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure what you mean in this phrase :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, if you add LocalTile overlay into the map in Android, you can still see that google map loads tiles underneath your 'offline' tiles (if device is online). That can go against the whole reason why offline tiles are used in the first place.

Copy link
Contributor

@alvelig alvelig Dec 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does mapType={"none"} help?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zavadpe maybe you should explain that in the file like you did for me? 😊

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it helped me in my project :) I'm using this snippet:
mapType={Platform.OS == "android" ? "none" : "standard"}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rborn ok, I'll add more thorough explanation ;)

Copy link
Contributor

@alvelig alvelig Dec 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thank you!
mapType={Platform.OS == "android" ? "none" : "standard"} please add it to the docs to avoid future issues like those mentioned in #1880.

BTW I was thinking about putting to roadmap android support for OSM, just for this cases #773, what do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alvelig But are you thinking to add OSM support within google MapView or something separate? If not google than osmdroid can be of huge help/inspiration.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point is to replace GoogleMaps not to be bound to GMaps license agreement. Not saying that we will do it right now, but just a way to go.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alvelig Get it, sounds like fun :)

@rborn
Copy link
Collaborator

rborn commented Dec 14, 2017

LGTM, @alvelig ?

@alvelig
Copy link
Contributor

alvelig commented Dec 14, 2017

LGTM. Thank you @zavadpe! BTW do you mind helping us triage the issues?

@rborn rborn merged commit f1126a4 into react-native-maps:master Dec 14, 2017
@zavadpe
Copy link
Contributor Author

zavadpe commented Dec 14, 2017

@alvelig @rborn No problem, glad to help. What's the process with triaging?

@alvelig
Copy link
Contributor

alvelig commented Dec 14, 2017

@zavadpe We could define it actually @christopherdro should we follow any rules defined before? Or we are to define our own process?

@zavadpe
Copy link
Contributor Author

zavadpe commented Feb 26, 2018

@type88 It seems you are trying to load offline tiles directly from your project structure. You need to store them locally in your device and load them using absolute path. I recommend to unzip them from assets on first start or something similar.

@type88
Copy link

type88 commented Feb 26, 2018

@zavadpe Thanks for this. Your solution presents one more riddle for me to solve. I've never done what you are suggesting, can you recommend a starting point or any working examples? Do I need to install something like react-native-fs or zip?

@zavadpe
Copy link
Contributor Author

zavadpe commented Feb 26, 2018

@type88 Yes, exactly. In a project I'm working on I for example use react-native-fs together with react-native-zip-archive to unzip tiles archive I'm downloading from our API. Once tiles are stored locally in a device, it should work as expected.

@Stophface
Copy link
Contributor

Stophface commented Mar 7, 2018

@zavadpe is there a way to use your implementation with MBTiles? MBTiles follows the {z}/{x}/{y} structure. The only thing I am not sure about is how to feed the extracted data from the database back to <LocalTile/>.

The {z}{x}{y} values can be retrieved using url(forTilePath:). However, how to feed the data back from the database to <LocalTile />?

@Stophface
Copy link
Contributor

@386sx any progress on this?

@zavadpe
Copy link
Contributor Author

zavadpe commented Mar 7, 2018

@Stophface Currently, MBTiles can not be used with LocalTile since it's not supported on native side (only tiles stored in file system). I suppose support could be added to read MBTiles file path and extract tiles on the go, but I didn't try that. Once I have more time, I can look at it.

@Stophface
Copy link
Contributor

Stophface commented Mar 7, 2018

@zavadpe I found that: https://github.com/jjlabella/CustomTileOverlay. I think

are of interest (assuming accessing the database would be done on the JavSscript side). So theoretically it is possible to read MBTiles into the MKTileOverlay. The {z}{x}{y} path is used to access the MBTiles database. The tile in question is extracted and then fed back to the MKTileOverLay as base64, using loadTile - if I understood everything correct.

The way I see it we could extend your <LocalTile />? Accessing the database from the JavaSciprt side should be easy, thus feeding the tiles to <LocalTile />extended with loadTile in base64 too - presupposed we receive the {z}{x}{y} on the JavaScript side (which should be possible through url(forTilePath)).

On the other hand: Accessing the MBTiles database from the JavaScript side and sending the base64 string to loadTile over the React-Native Bridge might not be a good idea. That could turn out to be a bottleneck... That in turn would imply that the database must be accessed natively...

@zavadpe
Copy link
Contributor Author

zavadpe commented Mar 7, 2018

@Stophface Reading MBTiles file (with the help of the repo you found) and passing the data to MKTileOverlay shouldn't be a problem (just check AIRMapLocalTileOverlay.m loadTileAtPath method). It all must happen on native side with similar implementation on android. As for data format obtained from MBTiles file, it should be the same.

@Stophface
Copy link
Contributor

Stophface commented Mar 7, 2018

@zavadpe It definitely will be a problem for me because I never wrote anything in Swift/Objecitve-C/Java before - only JavaScript and Python. However, I am happy to give it a try. Managing large datasets unbundled on the native file system is a pain. And I think a lot of other users would profit from being able to use MBTiles :-). Could I ask for instructions/finger points if I am stuck? That would help me a lot.

@Stophface
Copy link
Contributor

Stophface commented Mar 26, 2018

@zavadpe Sooo, I forked the repo v0.20.1 (because of that problem #2051 I used v0.20.1). All the code I edit will go into it. If successful, I will pull my changes into the repo I forked and make a pull request to this repo.

After investigating your LocalTile and the way the react-native-maps project is set up (that took a while), I think its better to reuse some of your code but create a different component. Something like <MbTilesOverlay />. It might be better to move further conversation into the forked repo because this is your PullRequest with a different topic?!

@zavadpe
Copy link
Contributor Author

zavadpe commented Mar 26, 2018

@Stophface Yeah, I also think it could be separate component. Feel free to add me in loop, when you have something working (or opened PR from your fork to upstream). Good luck!

@Stophface
Copy link
Contributor

@zavadpe Could you have a look at this https://github.com/Stophface/react-native-maps-0.20.1/issues/1?

I am not sure how to make the component I created "known" to react-natve. I think for someone who did this already its very easy. I just need a finger point...

@h3ll0w0rld123
Copy link

h3ll0w0rld123 commented Mar 28, 2018

@zavadpe any tips on how to test this with Expo? (Sorry kind of new to react-native programming.)

I'm running the Expo app on an iOS simulator and my android and not sure how to put the file in my local storage to access it. On my android I put the directory with the tiles in the right format in my downloads folder and try to access the path with '/storage/emulated/0/Download/{z}/{x}/{y}.png' but no luck--it just shows as blank with no map. I believe the expo app should have file permissions?

@Stophface
Copy link
Contributor

@h3ll0w0rld123 Can you access the files on a different way? Maybe your path is wrong. Try accessing the images not through <LocalTile /> but through another component, maybe the native <Image /> or something?!

@h3ll0w0rld123
Copy link

h3ll0w0rld123 commented Mar 28, 2018

@Stophface Good idea, just tried it and looks like it's not reading it. Is it a permissions thing?

Image code:

<Image
  style={{width: 66, height: 58}}
  source={{uri:'file:///storage/emulated/0/Download/sf/13/1306/3164.png'}}
 />

Map code:

    <MapView
          style={{ flex: 1 }}
          initialRegion={this.state.region}
          mapType={Platform.OS == "android" ? "none" : "standard"}
    >
      <LocalTile
        pathTemplate="/storage/emulated/0/Download/sf/{z}/{x}/{y}.png"
        tileSize={49}
      />
    </MapView>

How are you guys testing local storage?

@Stophface
Copy link
Contributor

Stophface commented Mar 29, 2018

@h3ll0w0rld123 I never worked with expo... Maybe its a permission problem, maybe the path is not correct. But now you know that its not a <LocalTile /> problem which narrows it down a bit :)

I access the file system with https://github.com/wkh237/react-native-fetch-blob. Maybe that helps you. I test local storage on the device, through accessing the files stored locally -- just as it would work when it goes into production.

@zavadpe
Copy link
Contributor Author

zavadpe commented Mar 29, 2018

@Stophface, @h3ll0w0rld123 Sorry guys, I'm busy these days and most of the time not near my computer. If file:///storage/emulated/... uri doesn't work for Image it's probably some permission problem. For LocalTile my pathTemplate is like this: RNFS.DocumentDirectoryPath+'/map/{z}/{x}/{y}.png' since I use react-native-fs for downloading my tiles from API and storing it in application private directory. This way it's usually easier since you don't have to deal with any permissions and user too... I myself don't have experience with expo so can't really help with that.

@h3ll0w0rld123
Copy link

@zavadpe No worries and thanks for doing this!

So I got past the permissions issue, turns out Expo has a similar Filesystem. I'm able to add it to the file directory in the iOS simulator, and verified the app can read it via image, but it's still not working with LocalTile.

Image now working:

  <Image
    style={{width: 66, height: 58}}
    source={{uri:FileSystem.documentDirectory + 'sf/13/1306/3164.png'}}
  />

screen shot 2018-03-29 at 5 54 41 pm

But this is not working:

<LocalTile
  pathTemplate={FileSystem.documentDirectory + "sf/{z}/{x}/{y}.png"}
  tileSize={256}
/>

What is tileSize and does that matter? The doc says The size of provided local tiles (usually 256 or 512)., but I'm not sure if this is the file size in bytes (which seems to be variable) or the count of tiles. I looked at the code and it seems to be byte size, but this looks to be variable on each image?

@Stophface
Copy link
Contributor

Stophface commented Mar 29, 2018

@h3ll0w0rld123 Nice! The TileSize matters. Its either 256 or 512 Pixel - depending how your tiles got created. Here a short explanation of the difference. If in doubt about your tile size: Normally tiles are created 256 x 256 Pixels.

Looking at your screenshot it looks to me like you need an API Key. Are you using google maps? If so, you need an API key - even for offline maps. See the installation instructions of react-native-maps on more information on that. Also make sure that you have the Google Playservices installed when on an Android Device.

@h3ll0w0rld123
Copy link

Thanks @Stophface, good to know yep it is indeed 256 pixels.

So I don't think it's an API issue. I used the JTileDownloader and selected OpenCycleMap, and that image just has a water mark on it (my thoughts are that it should still work). Unless I'm missing something?

@type88
Copy link

type88 commented Mar 29, 2018

Apologies if this isn't the best place to ask this, but would it be possible to add support for webp format tiles? It works in android but not iOS with the react-native-webp-support package.

From the package:
Usage
You don't have to do anything other than use WebP images. This project adds a custom RCTImageDataDecoder to your project, so all react-native components should be able to use WebP files. If you are using custom components that work with UIImages directly (without using RCTImageDataDecoder) you will have to change that code.

@h3ll0w0rld123
Copy link

h3ll0w0rld123 commented Mar 29, 2018

Related to my thing and for anyone else using Expo, looks like urltile works. But regardless, localtile is also awesome, great work!

@Stophface
Copy link
Contributor

Stophface commented Apr 18, 2018

@zavadpe I submited my PR with MBTiles support for Google Specification #2208

It would be nice to pass a prop through which specification schema for the tiles is used. There is Google specification and TMS specification (https://gist.github.com/tmcw/4954720).
If the TMSspecification is used, the y mus be transformed like so

iOS

NSInteger y = (int)pow((double)2, (double)path.z) - path.y - 1;

Soo integration here (line 28)

while on Android these two lines are needed

Android

Double yDouble = Math.pow(2, zoom) - y - 1;
y = yDouble.intValue();

See integration here (line 56-57)

A simple if/else would do

<MapView.MbTile
        pathTemplate={"{z}/{x}/{y}"}
        tileSize={256}
        schema={"tms"} />

and then (pseudo code)

if schema === "tms" y =  transform(y);
else y;

However, for me its pretty hard to add a new variable from JavaScript and pass it all the way through to these two files:

https://github.com/Stophface/react-native-maps-0.20.1/blob/dev/lib/ios/AirMaps/AIRMapMbTileOverlay.m

and

https://github.com/Stophface/react-native-maps-0.20.1/blob/dev/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapMbTile.java

Wanna contribute?
Your LocalTile would profit from that too. It allows for more flexibility.

@h3ll0w0rld123
Copy link

h3ll0w0rld123 commented Apr 18, 2018

Thanks for the PR @Stophface. I'm eagerly awaiting it! I made some comments on there, hope to learn more about the code.

Regarding the TMS format, for local tiles wouldn't a solution be for the developer to manually convert their local tiles from TMS to XYZ? It's a one time thing and can be automated, so I wouldn't worry about it too much? The only way I can see this being a problem is if the file is downloaded from a 3rd party source, but I'd argue that for this type of thing self hosting a custom version would be better anyway.

@hariDasu
Copy link

hariDasu commented Jan 10, 2019

Related to my thing and for anyone else using Expo, looks like urltile works. But regardless, localtile is also awesome, great work!

@h3ll0w0rld123 can you share how you got UrlTile to work with the localTiles? I followed your same errors entirely, from permissions and loading an image on top of the map successfully from the tilesPath, but no matter whether i use LocalTile or UrlTile this doesn't seem to be working. My only guess is that the format of the tiles is not mapping correctly.

My tiles are in folders representing the zoom levels. (0, 1, 2, 3) were originally working in a webView with react-leaflet on native, but I am trying to migrate this code into the react-native maps for a more debuggable & testable interface.

My tiles path is:

 const tilesPath = `file://${
      RNFS.DocumentDirectoryPath
    }/projects/37/pdf/tiles/32/{z}/{x}/{y}.png`;

And I have tried both of the following

    <LocalTile pathTemplate={tilesPath} tileSize={256} zIndex={-1} />
      <UrlTile pathTemplate={tilesPath} tileSize={256} zIndex={-1} />

Sorry if this comment is a bit StackOverflow-esque, but the steps you have gone through seems identical to the steps I have taken thus far... there must be something minor I am missing, if it is not the folder structure for zoom levels.

@systemlevel
Copy link

@type88 Yes, exactly. In a project I'm working on I for example use react-native-fs together with react-native-zip-archive to unzip tiles archive I'm downloading from our API. Once tiles are stored locally in a device, it should work as expected.

@zavadpe Peter thank you for your insights into this. I’m having some issues implementing this. Do you have some quick code examples you could share in your working usage of LocalTiles?

@systemlevel
Copy link

@hariDasu any luck getting yours working?

@hariDasu
Copy link

@systemlevel no, but i have abandoned this for a while and have stuck with using [https://github.com/react-native-community/react-native-webview](rn-community webview)
and importing leaflet and my unzipped tiles and rendering this way. when i have more time to reimplement this i will try again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.