void GeoJSONSource::Impl::load(FileSource& fileSource) {
    if (!urlOrGeoJSON.is<std::string>()) {
        loaded = true;
        return;
    }

    if (req) {
        return;
    }

    const std::string& url = urlOrGeoJSON.get<std::string>();
    req = fileSource.request(Resource::source(url), [this](Response res) {
        if (res.error) {
            observer->onSourceError(
                base, std::make_exception_ptr(std::runtime_error(res.error->message)));
        } else if (res.notModified) {
            return;
        } else if (res.noContent) {
            observer->onSourceError(
                base, std::make_exception_ptr(std::runtime_error("unexpectedly empty GeoJSON")));
        } else {
            rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> d;
            d.Parse<0>(res.data->c_str());

            if (d.HasParseError()) {
                std::stringstream message;
                message << d.GetErrorOffset() << " - "
                        << rapidjson::GetParseError_En(d.GetParseError());
                observer->onSourceError(base,
                                        std::make_exception_ptr(std::runtime_error(message.str())));
                return;
            }

            invalidateTiles();

            conversion::Result<GeoJSON> geoJSON = conversion::convertGeoJSON<JSValue>(d);
            if (!geoJSON) {
                Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s",
                           geoJSON.error().message.c_str());
                // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for
                // tiles to load.
                setGeoJSON(GeoJSON{ FeatureCollection{} });
            } else {
                setGeoJSON(*geoJSON);
            }

            loaded = true;
            observer->onSourceLoaded(base);
        }
    });
}
Esempio n. 2
0
static optional<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id,
                                                              const Convertible& value,
                                                              Error& error) {
    auto dataValue = objectMember(value, "data");
    if (!dataValue) {
        error = { "GeoJSON source must have a data value" };
        return {};
    }

    optional<GeoJSONOptions> options = convert<GeoJSONOptions>(value, error);
    if (!options) {
        return {};
    }

    auto result = std::make_unique<GeoJSONSource>(id, *options);

    if (isObject(*dataValue)) {
        optional<GeoJSON> geoJSON = convert<GeoJSON>(*dataValue, error);
        if (!geoJSON) {
            return {};
        }
        result->setGeoJSON(std::move(*geoJSON));
    } else if (toString(*dataValue)) {
        result->setURL(*toString(*dataValue));
    } else {
        error = { "GeoJSON data must be a URL or an object" };
        return {};
    }

    return { std::move(result) };
}