/// <summary> /// Deserializes a GeocodeFeed from an XML stream. /// </summary> /// <param name="stream">GeocodeFeed XML feed stream.</param> /// <returns>A GeocodeFeed that has been parsed from a stream.</returns> public static Task <GeocodeFeed> ReadAsync(Stream stream) { return(Task.Run <GeocodeFeed>(() => { GeocodeFeed feed = null; var ms = new MemoryStream(); try { stream.CopyTo(ms); if (BingMapsSDSToolkit.Internal.XmlUtilities.IsStreamCompressed(ms)) { //Uncompress the first file in the zipped stream. var zipArchive = new System.IO.Compression.ZipArchive(ms); if (zipArchive.Entries != null && zipArchive.Entries.Count > 0) { stream = zipArchive.Entries[0].Open(); ms = new MemoryStream(); stream.CopyTo(ms); } } ms.Position = 0; var serializer = new XmlSerializer(typeof(GeocodeFeed), "http://schemas.microsoft.com/search/local/2010/5/geocode"); feed = (GeocodeFeed)serializer.Deserialize(ms); } catch { } ms.Dispose(); return feed; })); }
private static GeocodeFeed ParseDelimitedFile(StreamReader textReader, BatchFileFormat format) { char delimiter; switch (format) { case BatchFileFormat.PIPE: delimiter = '|'; break; case BatchFileFormat.TAB: delimiter = '\t'; break; case BatchFileFormat.CSV: default: delimiter = ','; break; } var feed = new GeocodeFeed(); double schemaVersion = 1.0; using (var reader = new DelimitedFileReader(textReader, delimiter)) { var row = reader.GetNextRow(); if (row != null && row.Count > 0) { //Parse Schema Version info. if (row[0].StartsWith("Bing Spatial Data Services", StringComparison.OrdinalIgnoreCase) && row.Count >= 2) { double.TryParse(row[1], out schemaVersion); row = reader.GetNextRow(); } //Skip header if (string.Compare(row[0], "Id", StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(row[0], "GeocodeEntity/@Id", StringComparison.OrdinalIgnoreCase) == 0) { row = reader.GetNextRow(); } //Parse rows while (row != null && row.Count > 0) { var parsedRow = ParseRow(row, schemaVersion); if (parsedRow != null) { feed.Entities.Add(parsedRow); } row = reader.GetNextRow(); } } } return(feed); }
/// <summary> /// Creates a geocode dataflow job and uploads spatial data to process. /// </summary> /// <param name="dataFeed">The address data to geocode.</param> /// <param name="bingMapsKey">The Bing Maps Key to use for this job. The same key is used to get job status and download results.</param> /// <returns>A URL that defines the location of the geocode dataflow job that was created.</returns> private async Task <string> CreateJob(GeocodeFeed dataFeed, string bingMapsKey) { var tcs = new TaskCompletionSource <string>(); //Build the HTTP URI that will upload and create the geocode dataflow job Uri createJobUri = new Uri("https://spatial.virtualearth.net/REST/v1/dataflows/geocode?input=xml&clientApi=SDSToolkit&key=" + bingMapsKey); //Include the data to geocode in the HTTP request var request = HttpWebRequest.Create(createJobUri); // The HTTP method must be 'POST'. request.Method = "POST"; //request.ContentType = "application/xml"; request.ContentType = "application/octet-stream"; using (var requestStream = await request.GetRequestStreamAsync()) { using (var zipStream = new GZipStream(requestStream, CompressionMode.Compress)) { await dataFeed.WriteAsync(zipStream); } } request.BeginGetResponse((a) => { var r = (HttpWebRequest)a.AsyncState; try{ using (var response = (HttpWebResponse)r.EndGetResponse(a)) { // If the job was created successfully, the status code should be // 201 (Created) and the 'Location' header should contain a URL // that defines the location of the new dataflow job. You use this // URL with the Bing Maps Key to query the status of your job. if (response.StatusCode != HttpStatusCode.Created) { tcs.SetException(new Exception("An HTTP error status code was encountered when creating the geocode job.")); } string dataflowJobLocation = response.Headers["Location"]; if (String.IsNullOrEmpty(dataflowJobLocation)) { tcs.SetException(new Exception("The 'Location' header is missing from the HTTP response when creating a goecode job.")); } tcs.SetResult(dataflowJobLocation); } } catch (Exception ex) { tcs.SetException(ex); } }, request); return(await tcs.Task); }
/// <summary> /// Downloads job results to files names Success.txt (successfully geocoded results) and Failed.txt (info about spatial data that was not geocoded successfully). /// </summary> /// <param name="statusDetails">Inclues job status and the URLs to use to download all geocoded results.</param> /// <param name="bingMapsKey">The Bing Maps Key for this job. The same key is used to create the job and get job status. </param> private async Task <BatchGeocoderResults> DownloadResults(DownloadDetails statusDetails, string bingMapsKey) { var results = new BatchGeocoderResults(); //Write the results for data that was geocoded successfully to a file named Success.xml if (statusDetails.SucceededUrl != null && !statusDetails.SucceededUrl.Equals(String.Empty)) { //Create a request to download successfully geocoded data. You must add the Bing Maps Key to the //download location URL provided in the response to the job status request. var successUriBuilder = new UriBuilder(statusDetails.SucceededUrl + "?clientApi=SDSToolkit&key=" + bingMapsKey); var successfulRequest = (HttpWebRequest)WebRequest.Create(successUriBuilder.Uri); successfulRequest.Method = "GET"; using (var response = (HttpWebResponse)await successfulRequest.GetResponseAsync()) { if (response.StatusCode != HttpStatusCode.OK) { throw new Exception("An HTTP error status code was encountered when downloading results."); } using (var receiveStream = response.GetResponseStream()) { results.Succeeded = await GeocodeFeed.ReadAsync(receiveStream); } } } //If some spatial data could not be geocoded, write the error information to a file called Failed.xml if (statusDetails.FailedUrl != null && !statusDetails.FailedUrl.Equals(String.Empty)) { var failedRequest = (HttpWebRequest)WebRequest.Create(new Uri(statusDetails.FailedUrl + "?clientApi=SDSToolkit&key=" + bingMapsKey)); failedRequest.Method = "GET"; using (var response = (HttpWebResponse)await failedRequest.GetResponseAsync()) { if (response.StatusCode != HttpStatusCode.OK) { throw new Exception("An HTTP error status code was encountered when downloading results."); } using (Stream receiveStream = response.GetResponseStream()) { results.Failed = await GeocodeFeed.ReadAsync(receiveStream); } } } return(results); }
/// <summary> /// Method to geocode a set of data. /// </summary> /// <param name="dataFeed">GeocodeFeed which contains the data to batch geocode/reverse geocode.</param> /// <param name="bingMapsKey">Bing Maps key to use for accessing service.</param> /// <returns>The results of the batch geocoding process.</returns> public async Task <BatchGeocoderResults> Geocode(GeocodeFeed dataFeed, string bingMapsKey) { BatchGeocoderResults results; try { ReportStatus("Creating batch geocode job."); string dataflowJobLocation = await CreateJob(dataFeed, bingMapsKey); ReportStatus("Job created and being processed."); //Continue to check the dataflow job status until the job has completed var statusDetails = new DownloadDetails(); do { statusDetails = await CheckStatus(dataflowJobLocation, bingMapsKey); if (statusDetails.JobStatus == "Aborted") { ReportStatus("Batch geocode job aborted."); return(new BatchGeocoderResults() { Error = "Batch geocode job was aborted due to an error." }); } if (statusDetails.JobStatus.Equals("Pending")) { await Task.Delay(_statusUpdateInterval); } }while (statusDetails.JobStatus.Equals("Pending")); ReportStatus("Batch geocode job completed. Downloading results."); results = await DownloadResults(statusDetails, bingMapsKey); ReportStatus("Batch geocode results downloaded."); } catch (Exception ex) { results = new BatchGeocoderResults() { Error = ex.Message }; } return(results); }