Exemple #1
0
        protected override async Task Process()
        {
            //            _logger.LogDebug("GDALConversion process starting.");

            // For the enlightenment of other, later, readers:
            // ogr2ogr will be used to process not only obvious conversion sources (eg shape files) but also
            // geojson files. Why, you might ask, because tippecanoe can import GeoJSON directly? It's because
            // passing the GeoJSON through ogr2ogr will ensure that the final GeoJSON is in the correct projection
            // and that it should be valid GeoJSON as well.
            var gdalMsg = await _mapdata.GdConversionQueue.GetMessage();

            if (gdalMsg != null) // if no message, don't try
            {
                try
                {
                    ConversionMessageData gdalData;
                    try
                    {
                        gdalData = JsonConvert.DeserializeObject <ConversionMessageData>(gdalMsg.Content);
                    }
                    catch (JsonReaderException e)
                    {
                        _logger.LogError($"failed to decode JSON message on queue {e}");
                        return;
                    }

                    var start = DateTime.UtcNow;
                    _logger.LogDebug("About to convert Gdal");

                    var randomFolderName = Path.GetRandomFileName();
                    _logger.LogDebug($"folder name = {randomFolderName}");
                    // it will be in the system's temporary directory
                    var tempPath = Path.Combine(Path.GetTempPath(), randomFolderName);
                    _logger.LogDebug($"temporary path = {tempPath}");

                    // If the directory already existed, throw an error - this should never happen, but just in case.
                    if (Directory.Exists(tempPath))
                    {
                        _logger.LogError($"The temporary path '{tempPath}' already existed.");
                        throw new Exception("The temporary path already existed.");
                    }

                    // Try to create the directory.
                    Directory.CreateDirectory(tempPath);
                    _logger.LogDebug($"The directory was created successfully at {Directory.GetCreationTime(tempPath)}.");

                    // we need to keep source and dest separate in case there's a collision in filenames.
                    var sourcePath = Path.Combine(tempPath, "source");
                    Directory.CreateDirectory(sourcePath);

                    var destPath = Path.Combine(tempPath, "dest");
                    Directory.CreateDirectory(destPath);

                    if (gdalData.DataLocation != null) // if it was null we don't want to do anything except remove the job from queue
                    {
                        // retrieve the source data file from the supplied URI
                        var remoteUri = new Uri(gdalData.DataLocation);
                        // we will need to know if this is a zip file later so that we can tell GDAL to use the zip virtual file system.
                        var       isZip       = Path.GetExtension(remoteUri.AbsolutePath).ToUpper() == ".ZIP";
                        var       localFile   = Path.Combine(sourcePath, Path.GetFileName(remoteUri.AbsolutePath));
                        WebClient myWebClient = new WebClient();
                        _logger.LogDebug($"Downloading {gdalData.DataLocation} to {localFile}");
                        myWebClient.DownloadFile(gdalData.DataLocation, localFile);
                        List <string> filesToProcess = new List <string>();   // a list of files to convert
                                                                              // There could be multiple files if we were given a zip file - currently we look for shp and gdb.
                        if (isZip)
                        {
                            // if it was a zip file we will extract it and use the content as the input.
                            using (var zip = ZipFile.OpenRead(localFile))
                            {
                                zip.ExtractToDirectory(sourcePath);
                            }
                            // Shape files are handled differently because there are usually multiple files per shape and we don't want to process the supporting files.
                            filesToProcess.AddRange(Directory.GetFiles(sourcePath, "*.shp"));
                            // GDB is also processed differently because they are actually directories, not files, but are treated by GDAL like they were files.
                            filesToProcess.AddRange(Directory.GetDirectories(sourcePath, "*.gdb"));
                            // for the moment we'll add geojson too. There's bound to be others we'll want to support but this should get us going.
                            filesToProcess.AddRange(Directory.GetFiles(sourcePath, "*.geojson"));
                        }
                        else
                        {
                            // not a zip file so we will assume that it's a single file to convert.
                            filesToProcess.Add(localFile);
                        }

                        foreach (var sourceFile in filesToProcess)
                        {
                            var layerName = Path.GetFileNameWithoutExtension(sourceFile);

                            var geoJsonFile = Path.Combine(destPath, $"{layerName}.geojson");
                            var gdalProcess = new Process
                            {
                                StartInfo =
                                {
                                    FileName  = "ogr2ogr",
                                    Arguments =
                                        "-f \"GeoJSON\" " +       // always converting to GeoJSON
                                        $"-nln \"{layerName}\" " +
                                        "-t_srs \"EPSG:4326\" " + // always transform to WGS84
                                        $"{geoJsonFile} " +
                                        $"{sourceFile}",
                                    UseShellExecute        = false,
                                    RedirectStandardOutput = true,
                                    RedirectStandardError  = true,
                                    CreateNoWindow         = true
                                }
                            };
                            _logger.LogDebug($"ogr2ogr arguments are {gdalProcess.StartInfo.Arguments}");

                            gdalProcess.Start();
                            var errmsg = "";
                            while (!gdalProcess.StandardError.EndOfStream)
                            {
                                errmsg += gdalProcess.StandardError.ReadLine();
                            }
                            gdalProcess.WaitForExit();
                            var exitCode = gdalProcess.ExitCode;
                            _logger.LogDebug($"og2ogr returned exit code {exitCode}");
                            if (exitCode != 0)
                            {
                                _logger.LogError($"Spatial data to GeoJSON conversion failed (errcode={exitCode}), msgs = {errmsg}");
                                throw new Exception($"Spatial data to GeoJSON conversion failed. {errmsg}");
                            }


                            _logger.LogDebug($"geojson file is in {geoJsonFile}");
                            // now we need to put the converted geojson file into storage
                            var location = await _mapdata.GeojsonContainer.Store($"{layerName}.geojson", geoJsonFile);

                            _logger.LogDebug("Upload of geojson file to storage complete.");

                            var end      = DateTime.UtcNow;
                            var duration = end - start;
                            _logger.LogDebug($"GDALConversion took {duration.TotalMilliseconds} ms.");

                            // we created geoJson so we can put a request in for geojson to mvt conversion.
                            var mbRequestId = await _mapdata.CreateMbConversionRequest(new ConversionMessageData
                            {
                                DataLocation = location,
                                Description  = gdalData.Description,
                                LayerName    = layerName
                            });

                            await _mapdata.JobStatusTable.AddStatus(mbRequestId, gdalMsg.Id);
                        }
                    }
                    // we completed GDAL conversion and creation of MVT conversion request, so remove the GDAL request from the queue
                    _logger.LogDebug("deleting gdal message from queue");
                    await _mapdata.GdConversionQueue.DeleteMessage(gdalMsg);

                    await _mapdata.JobStatusTable.UpdateStatus(gdalMsg.Id, JobStatus.Finished);
                }
                catch (Exception)
                {
                    await _mapdata.JobStatusTable.UpdateStatus(gdalMsg.Id, JobStatus.Failed);

                    throw;
                }
            }
            await Task.Delay(_options.CheckConvertTime);
        }