0

I'm trying to access a .pbf file from the MainActivity class in an Xamarin Android. I tried adding the file to a new Maps folder, I also tried adding it to the Assets and Resources folders.

I've tried setting them with different build actions. However this error is always generated... "System.IO.FileNotFoundException: Could not find file"

using (var inputStream = 
   new FileInfo(@"Resources\ireland.osm.pbf").OpenRead())
{
...
}

How should the file be added to the project and accessed using FileInfo?

Ruairi
  • 35
  • 1
  • 7

1 Answers1

3

Firstly what you probably want to do is use Assets and follow this Tutorial

Including files as your application doesnt work the same as if you were to access them directly on the file system. Now you wont be able to get FileInfo from the asset but you will be able to read them.

First place the ireland.osm.pbf file in the Assets directory. Next you must (and usually does it for you if you choose Add Existing Item) set the Build Action to AndroidAsset. This builds (packages) the file as part of the Android Package.

Next Assets are loaded (accessed) through the AssetManager. Luckily this is available in the Activity Context through this.Assets.

To read the asset is quite simple.

using (StreamReader sr = new StreamReader (Assets.Open ("ireland.osm.pbf")))
{
    //do as you wish with the stream reader.
}

Edit:

From the comment.

but i need the FileInfo to implement the PBFOsmStreamSource() as shown here link

I had a quick look at the source code for PBFOsmStreamSource() and has the constructor:

/// <summary>
/// Creates a new source of PBF formated OSM data.
/// </summary>
/// <param name="stream"></param>
public PBFOsmStreamSource(Stream stream)
{
    _stream = stream;
}

Now stream reader is NOT a Stream we need to convert it to a Stream however does have the property BaseStream. Now given I dont have a test enviroment you should be able to call.

using (StreamReader sr = new StreamReader(Assets.Open("ireland.osm.pbf")))
{
    //do as you wish with the stream reader.
    var pbfStreamSource = new PBFOsmStreamSource(sr.BaseStream);

}

EDIT 2

StreamReader.BaseStream() does not support the Seek() method. Reading further on the source for PBFOsmStreamSource shows.

private void Initialize()
{
    _stream.Seek(0, SeekOrigin.Begin);

    this.InitializePBFReader();
}

Which is why you are getting the System.NotSupporte‌​dException exception. Therefore you will need to construct a stream that supports the Seek() method. Fortunatly MemoryStream is capable of changing the position and supports the Seek() method.

Again, I do not have a test enviroment.

using (StreamReader sr = new StreamReader(Assets.Open("ireland.osm.pbf")))
{
    using (var ms = new MemoryStream())
    {
        sr.BaseStream.CopyTo(ms);
        var pbfStreamSource = new PBFOsmStreamSource(ms);
    }
}

Final Edit

After more review and looking at their samples I have written an example console application that uses StreamReader in the same fasion as you would on the mobile. Now I have tweaked this slightly so we can dispose of the StreamReader quickly as these files consume a large amount of memory and there is no need to keep both streams open at once.

using OsmSharp.Geo;
using OsmSharp.Osm.PBF.Streams;
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;

namespace Sample.GeometryStream
{
    class Program
    {
        static void Main(string[] args)
        {
            Test();
            Console.ReadLine();

        }
        static async Task Test()
        {

            await ToFile("http://files.itinero.tech/data/OSM/planet/europe/luxembourg-latest.osm.pbf", "test.pbf");

            var sr = new StreamReader("test.pbf");
            using (var ms = new MemoryStream())
            {
                try
                {
                    sr.BaseStream.CopyTo(ms);
                }
                catch
                {
                    //TODO: Handle exceptions
                }
                finally
                {
                    sr.Close();
                    sr.Dispose();
                }
                var pbfStreamSource = new PBFOsmStreamSource(ms);
            }
        }

        public static async Task ToFile(string url, string filename)
        {
            if (!File.Exists(filename))
            {
                var client = new HttpClient();
                using (var stream = await client.GetStreamAsync(url))
                using (var outputStream = File.OpenWrite(filename))
                {
                    stream.CopyTo(outputStream);
                }
            }
        }

    }
}
Nico
  • 12,493
  • 5
  • 42
  • 62
  • This allows me to access the file but i need the FileInfo to implement the PBFOsmStreamSource() as shown here [link](https://github.com/OsmSharp/ui/wiki/A-getting-started-example) – Ruairi Aug 17 '16 at 13:39
  • @Rani I have updated the response. Have a look at `BaseStream` property of `StreamReader` – Nico Aug 17 '16 at 13:52
  • Tried your suggestion. Got this error but it may be unrelated... _{System.NotSupportedException: Specified method is not supported. at Android.Runtime.InputStreamInvoker.Seek (Int64 offset, SeekOrigin origin) [0x00000] in /Users/builder/data/lanes/3415/7db2aac3/source/monodroid/src/Mono.Android/src/Runtime/InputStreamInvoker.cs:38 at OsmSharp.Osm.PBF.Streams.PBFOsmStreamSource.Initialize () [0x00001] in :0 at OsmSharp.Osm.Streams.Filters.OsmStreamFilterTagsFilter.Initialize () [0x00007] in :0 at OsmSharp.Osm.Streams.OsmStreamTarget.Pull ()_ – Ruairi Aug 17 '16 at 14:05
  • Ok, so somewhere its trying to invoke the `Seek` method on the stream and the `BaseStream` probably doesnt support that. Therefore we need a different type of stream that does support `Seek`. One that comes to mind is `MemoryStream`. Again will update the response. – Nico Aug 17 '16 at 14:13
  • @Ruairi final edit posted. The solution should work properly for you. Cheers Nico – Nico Aug 17 '16 at 14:39
  • Yes great thanks @Nico. Also found another way of doing it using 'map.AddLayer(new LayerMBTile(OsmSharp.Android.UI.Data.SQLite.SQLiteConnection.CreateFrom( Assembly.GetExecutingAssembly().GetManifestResourceStream(@"OsmSharp.Android.UI.Sample.kempen.mbtiles"), "map")));' Details here [http://stackoverflow.com/questions/21637830/getmanifestresourcestream-returns-null] – Ruairi Aug 18 '16 at 14:20