【问题标题】:Synchronous geocoding in NodeJSNodeJS 中的同步地理编码
【发布时间】:2016-05-15 15:02:37
【问题描述】:

是否可以等待异步调用(geocode.reverseGeocode)?我想循环浏览目录中的 JPG 文件,从其 EXIF 数据中获取纬度和经度,并反向地理编码以获取拍摄这些照片的国家/城市。最后,它需要在文件中添加一个 OSX 标签。但是我需要多次运行 NodeJS 脚本来获取所有标签。 (因为文件迭代器不等待地理编码过程)。等待此请求的最佳解决方案是什么?

你可以在这里找到我的代码: https://github.com/Acoustics/jbTagify/blob/development/index.js

提前致谢

【问题讨论】:

标签: javascript node.js asynchronous geocode


【解决方案1】:

处理多个异步调用的一种简单方法是使用 Promise,无需同步运行它们并等待一个完成。您可以使用 Node 中可用的本机承诺,也可以使用诸如 bluebird 之类的承诺库,它可以promisify其他执行异步操作的库。

最简单的用例看起来像

var Promise = require("bluebird");
var geocoder = Promise.promisifyAll(require('geocoder'))

geocoder.geocodeAsync("Atlanta, GA")
.then(function(data){
    var lat = data.results[0].geometry.location.lat;
    var lng = data.results[0].geometry.location.lng;
    console.log("Coordinates for Atlanta, GA: " + lat + "," + lng);
});

你使用了 Promisified 函数 geocodeAsync(原始函数名 + Async),它返回一个以返回数据作为解析值的承诺。

在您想要执行多个异步代码的情况下,您可以轻松地创建一个 Promise 数组,然后让它们并行运行,并在解决所有 Promise 时处理结果。

var citiesToGeocode = ["Miami, FL", "New York", "Orlando, FL", "Rio de Janeiro, Brazil"];
var geocodePromises = [];
for (var i = 0; i < citiesToGeocode.length-1; ++i) {
    geocodePromises.push(geocoder.geocodeAsync(citiesToGeocode[i]));
}

Promise.all(geocodePromises).then(function(result) {
    result.forEach(function(geocodeResponse){
        console.log("Coordinates for " + geocodeResponse.results[0].formatted_address +": " + geocodeResponse.results[0].geometry.location.lat + "," + geocodeResponse.results[0].geometry.location.lng);
    });
});

使用相同的方法,您可以使用reverseGeocodeAsync 从坐标中查找信息。

【讨论】:

  • 谢谢!我明白它背后的想法,但是:我需要在另一个回调中调用“geocodePromises.push”(来自新 exif 的回调)。我用 jpg 遍历一个目录(异步),用新的 exif(异步)获取 exif 数据,最后,我想向 promises-array(在 fs.readDir 之外)添加一个 promise。在我的函数 Promise.all 中,我想遍历每个包含位置数据和文件名的承诺。我怎么能做到这一点? (我尝试将 promise.promisifyAll 包裹在 require('exif').ExifImage 但没有成功。
  • exif 包似乎不适用于异步代码,那么为什么必须将它包装在 promise 中? Promise.all 将使用结果解析一个数组,您可以循环遍历该数组,然后做任何您想做的事情。也许这是您应该提出的另一个问题。
  • 你拯救了我的一天 :)
【解决方案2】:

这是我的代码。如您所见,我需要一个根级别的 Promise 集合。我的目标是获得一组 promise,其中包含文件名及其地理编码数据。

var
    _ = require('lodash'),
    path = require('path'),
    pkg = require(path.join(__dirname, 'package.json')),
    fs = require('fs'),
    promise = require('bluebird'),
    exif = require('exif').ExifImage,
    geocoder = promise.promisifyAll(require('geocoder')),
    exec = require('child_process').exec,
    config = require('nconf').file({
        file: path.join(__dirname, 'config.json')
    });

var geocodePromises = []; // I want to collect all promises here

fs.readdir(path.join(__dirname, 'files'), function (err, files) {
    files.filter(function (file) {
        var ext = file.split('.').pop(),
            cond;

        cond = file; // File needs to exist
        cond = cond && file[0] != '.'; // Exclude hidden files
        cond = cond && config.get("files:support").indexOf(ext) > -1; // File needs to be supported

        return cond;
    }).forEach(function (file) {
        file = path.join(__dirname, 'files/') + file;

        new exif({
            image: file
        }, function (err, data) {
            if (!err) {
                if (data.gps) {
                    var location = parseGPS(data.gps); // Obtain lat & lng

                    geocodePromises.push(geocoder.reverseGeocodeAsync(location.lat, location.lng));
                } else {
                    console.log("Error: GPS data not available");
                }
            } else {
                console.log("Error: " + err);
            }
        });
    });

    Promise.all(geocodePromises).then(function (result) {
        console.log(result); // Returns an empty array []
        // Each result should contain the filename and geocoded location

        /*
            Example:

            {
                "file": "1.jpg",
                "location": {
                    <google reverseGeocode object>
                }
            }
        */
    });

    //exec('killall Finder');
});

function parseGPS(gps) {
    var lat = gps.GPSLatitude,
        lng = gps.GPSLongitude,
        latRef = gps.GPSLatitudeRef || "N",
        lngRef = gps.GPSLongitudeRef || "W";

    // Convert coordinates to WGS84 decimal
    lat = (lat[0] + lat[1] / 60 + lat[2] / 3600) * (latRef == "N" ? 1 : -1);
    lng = (lng[0] + lng[1] / 60 + lng[2] / 3600) * (lngRef == "W" ? -1 : 1);

    // Return lat, lng
    return {
        lat,
        lng
    };
};

【讨论】:

  • 如果您想提供更多信息,请编辑您的问题,不要发布答案。
  • 有人可以解释一下我的例子吗?
猜你喜欢
  • 2010-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多