I am working on The XamWebMap control to render a set of big shapefiles. I would like to optimize my application so i could download the shapefiles in ziped format and put it in the Isolated storage, and to re use the same file over the life of the application. However the Shapefile reader only takes URI. Is there a way i could make it read from the Isolated storage? Are there any other optimization techniques i could use to avoid downloading of the big shapefiles?
Many Thanks
A point I have not elaborated, I am also interested in creating an out of browser application once i have the In browser Experience in place. So that is also another reason why I need the loading from the Isolated Storage.
I don't think its possible, currently, to read a file out of isolated storage using a URI, and I'm not sure if there is any way to register your own URI handlers with Silverlight. You're best bet would probably be to make a feature request to be able to provide a stream to ShapeFileReader. http://devcenter.infragistics.com/protected/requestfeature.aspx
If you are feeling courageous you could try using the SQLShapeReader which takes an IEnumerable. You can see the SqlShapeReader help for more info, but this would require that you converted your shape files into that format, and that you could serialize the data into files in isolated storage, so the feature request may be a better fit, depending on your situation.
There may be some way of redirecting a URI get to the isolated storage, but I haven't come accross one yet. Make sure to let us know if you do!
-Graham
Actually, it turns out you can redirect URI requests by registering custom prefixes in Silverlight 3 and later, here's a sample I've worked up that should get you started. You will notice two buttons, one that downloads the map files into isolated storage, and the other that will load the shape files from isolated storage using the ShapeFileReader:
The Xaml:
<Grid x:Name="LayoutRoot"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition /> </Grid.RowDefinitions> <Button Grid.Row="0" x:Name="download" Click="download_Click" Content="Download Map" /> <Button Grid.Row="1" x:Name="loadFromIso" Click="loadFromIso_Click" Content="Load from ISO" /> <igMap:XamWebMap Grid.Row="2" x:Name="theMap"> <igMap:XamWebMap.Layers> <igMap:MapLayer x:Name="layer1" /> </igMap:XamWebMap.Layers> </igMap:XamWebMap> </Grid>
The code behind:
public partial class MainPage : UserControl { public MainPage() { InitializeComponent(); WebRequest.RegisterPrefix("iso", new IsoStorageRequestCreator()); } private void download_Click(object sender, RoutedEventArgs e) { wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted); wc.OpenReadAsync(new Uri("ShapeFiles/usa_st.shp", UriKind.RelativeOrAbsolute), "usa_st.shp"); } private WebClient wc = new WebClient(); void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) { using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication()) { using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(e.UserState.ToString(), FileMode.Create, isf)) { using (StreamWriter sw = new StreamWriter(isfs)) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = e.Result.Read(buffer, 0, buffer.Length)) != 0) { isfs.Write(buffer, 0, bytesRead); } } } } if (e.UserState.ToString().EndsWith("shp")) { wc.OpenReadAsync(new Uri("ShapeFiles/usa_st.dbf", UriKind.RelativeOrAbsolute), "usa_st.dbf"); } } private void loadFromIso_Click(object sender, RoutedEventArgs e) { ShapeFileReader reader = new ShapeFileReader(); reader.Uri = "iso://usa_st"; theMap.Layers[0].Reader = reader; theMap.Layers[0].Imported += new MapLayerImportEventHandler(MainPage_Imported); theMap.Layers[0].ImportAsync(); } void MainPage_Imported(object sender, MapLayerImportEventArgs e) { theMap.WindowFit(); } }
The important line here being:
WebRequest.RegisterPrefix("iso", new IsoStorageRequestCreator());
Which will register the iso:// prefix with our custom request creator which is defined as such:
public class IsoStorageRequestCreator : IWebRequestCreate { #region IWebRequestCreate Members public WebRequest Create(Uri uri) { return new IsoStorageRequest(uri); } #endregion }
Which this will make sure that for the iso scheme the WebClient will create IsoStorageRequests.The IsoStorageRequests will, in turn, create IsoStorageResponses, which will return streams for the appropriate files in Isolated storage.
public class IsoStorageRequest : WebRequest { public override Uri RequestUri { get { return requestUri; } } private readonly Uri requestUri; public IsoStorageRequest(Uri uri) { this.requestUri = uri; } public override void Abort() { throw new NotImplementedException(); } public override IAsyncResult BeginGetRequestStream( AsyncCallback callback, object state) { throw new NotImplementedException(); } public override IAsyncResult BeginGetResponse( AsyncCallback callback, object state) { IsoStorageAsyncResult result = new IsoStorageAsyncResult() { AsyncState = state }; callback(result); return result; } public override string ContentType { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public override Stream EndGetRequestStream( IAsyncResult asyncResult) { throw new NotImplementedException(); } public override WebResponse EndGetResponse( IAsyncResult asyncResult) { return new IsoStorageResponse(requestUri); } public override WebHeaderCollection Headers { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } public override string Method { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } } } public class IsoStorageResponse : WebResponse { private Uri uri; private long contentLength = 0; public IsoStorageResponse(Uri uri) { this.uri = uri; } public override long ContentLength { get { return contentLength; } } public override string ContentType { get { return @"application/octet-stream"; } } public override Stream GetResponseStream() { using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication()) { string file = uri.AbsoluteUri.Replace("iso://", ""); if (file.EndsWith("/")) { file = file.Substring(0, file.Length - 1); } using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream( file, FileMode.Open, isf)) { contentLength = isfs.Length; byte[] buffer = new byte[4096]; int bytesRead; MemoryStream mem = new MemoryStream(); while ((bytesRead = isfs.Read(buffer, 0, buffer.Length)) != 0) { mem.Write(buffer, 0, bytesRead); } mem.Seek(0, SeekOrigin.Begin); return mem; } } } public override void Close() { throw new NotImplementedException(); } public override Uri ResponseUri { get { return uri; } } } public class IsoStorageAsyncResult : IAsyncResult { #region IAsyncResult Members private object _asyncState; public object AsyncState { get { return _asyncState; } set { _asyncState = value; } } public System.Threading.WaitHandle AsyncWaitHandle { get { throw new NotImplementedException(); } } public bool CompletedSynchronously { get { throw new NotImplementedException(); } } public bool IsCompleted { get { return true; } } #endregion }
Let me know if you have any questions.-Graham
Hi All,
I am able to use the Isolated storage through the above code. But data mapping for the reader is not giving color code if i map to any one of my fields in the shape file. Can any one help me out in this issue.
Thanks in Advance,
Pavan
Unless I'm mistaken, the purpose of Isolated Storage Files is that you can't read them from another application. Its supposed to keep low trust apps from reading each others data. If you give high enough trust to an app, you may just be able to read the data off the file system though. But I'm pretty sure you aren't supposed to use the isolated storage system this way.
If you need global access to files from a silverlight application on a machine, you might either need to talk to a local web service that can give you the files, or make the app full trust and get them off the file system in some manner (COM?)
Hi... This post solved my problem..
But I have one more problem.. i.e How can i read existing Isolated Storage Files in other silverlight application.
My requirement is. Like i have One Application which is used to Create a Isolated Storage and Copy files into it and having one more Application which is used to Read the copied files..
Here I am Getting problem to Read Existing Files from Isolated Storage. Its Creating another Isolated Storage and reading files from this new isolated storage..
Please Solve it asap...
Thank u..