Beispiel #1
0
def get_choropleth_places(geoDcid):
    """ Get the list of places to show on a choropleth chart for a given place

    Args:
        geoDcid: dcid of the place of interest

    Returns:
        (list of dcids, property to use for fetching geo json)
    """
    place_list = []
    place_type = place_api.get_place_type(geoDcid)
    display_level = None
    if place_type in CHOROPLETH_DISPLAY_LEVEL_MAP:
        display_level = CHOROPLETH_DISPLAY_LEVEL_MAP[place_type]
    elif place_type in EQUIVALENT_PLACE_TYPES and EQUIVALENT_PLACE_TYPES[
            place_type] in CHOROPLETH_DISPLAY_LEVEL_MAP:
        place_type = EQUIVALENT_PLACE_TYPES[place_type]
        display_level = CHOROPLETH_DISPLAY_LEVEL_MAP[place_type]
    else:
        return place_list

    if place_type == display_level:
        parents_places = place_api.parent_places(geoDcid)
        for parent in parents_places.get(geoDcid, []):
            parent_dcid = parent.get('dcid', None)
            if not parent_dcid:
                continue
            parent_place_types = parent.get('types', [])
            for parent_place_type in parent_place_types:
                parent_display_level = CHOROPLETH_DISPLAY_LEVEL_MAP.get(
                    parent_place_type, None)
                if not parent_display_level:
                    parent_display_level = CHOROPLETH_DISPLAY_LEVEL_MAP.get(
                        EQUIVALENT_PLACE_TYPES.get(parent_place_type, ''))
                if parent_display_level == display_level:
                    place_list = dc_service.get_places_in([parent_dcid],
                                                          display_level).get(
                                                              parent_dcid, [])
                    geo_prop = CHOROPLETH_GEOJSON_PROPERTY_MAP[display_level]
                    # Puerto Rico (geoId/72) requires higher resolution geoJson
                    if parent_dcid == 'geoId/72':
                        geo_prop = 'geoJsonCoordinatesDP1'
                    return place_list, geo_prop
        return place_list
    else:
        place_list = dc_service.get_places_in([geoDcid],
                                              display_level).get(geoDcid, [])
        geo_prop = CHOROPLETH_GEOJSON_PROPERTY_MAP[display_level]
        # Puerto Rico (geoId/72) requires higher resolution geoJson
        if geoDcid == 'geoId/72':
            geo_prop = 'geoJsonCoordinatesDP1'
        return place_list, geo_prop
Beispiel #2
0
def child_statvars():
    """
    Gets all statistical variables available for a particular sublevel of a
        dcid.
    API Params:
        dcid: The place dcid to pull information for, as a string.
        level: The level of children to pull for, as a string. If omitted,
            then the subgeo is computed.
    Example Query:
        api/place/child/statvars?dcid=country/USA&level=State
        Returns all statistical variables that are value for states of the USA.
    Returns:
        A json list of all the available statistical variables.
    """
    # Get required params.
    dcid = flask.request.args.get("dcid")
    if not dcid:
        return flask.jsonify({"error": "Must provide a 'dcid' field!"}, 400)
    requested_level = get_sublevel(dcid, flask.request.args.get("level"))

    # Get sublevels.
    geos_contained_in_place = dc.get_places_in([dcid], requested_level)
    if dcid not in geos_contained_in_place:
        return flask.jsonify({"error": "Internal server error."}, 500)
    geos_contained_in_place = geos_contained_in_place[dcid]

    # Get all available Statistical Variables for subgeos.
    # Only the union of the first 10 geos are returned for speed.
    # TODO(iancostello): Determine whether this heuristic is too generous
    # or too restrictive.
    stat_vars_for_subgeo = set()
    for geoId in geos_contained_in_place[:10]:
        stat_vars_for_subgeo = stat_vars_for_subgeo.union(
            place.statsvars(geoId))
    return json.dumps(list(stat_vars_for_subgeo))
Beispiel #3
0
def geojson():
    """
    Get geoJson data for places enclosed within the given dcid
    """
    place_dcid = request.args.get("placeDcid")
    if not place_dcid:
        return Response(json.dumps("error: must provide a placeDcid field"),
                        400,
                        mimetype='application/json')
    place_type = request.args.get("placeType")
    if not place_type:
        place_dcid, place_type = get_choropleth_display_level(place_dcid)
    geos = []
    if place_dcid and place_type:
        geos = dc_service.get_places_in([place_dcid],
                                        place_type).get(place_dcid, [])
    if not geos:
        return Response(json.dumps({}), 200, mimetype='application/json')
    geojson_prop = CHOROPLETH_GEOJSON_PROPERTY_MAP.get(place_type, "")
    # geoId/72 needs higher resolution geojson because otherwise, the map looks
    # too fragmented
    if place_dcid == 'geoId/72':
        geojson_prop = 'geoJsonCoordinatesDP1'
    names_by_geo = place_api.get_display_name('^'.join(geos), g.locale)
    geojson_by_geo = dc_service.get_property_values(geos, geojson_prop)
    features = []
    for geo_id, json_text in geojson_by_geo.items():
        if json_text and geo_id in names_by_geo:
            geo_feature = {
                "type": "Feature",
                "geometry": {
                    "type": "MultiPolygon",
                },
                "id": geo_id,
                "properties": {
                    "name": names_by_geo.get(geo_id, "Unnamed Area"),
                    "geoDcid": geo_id,
                }
            }
            # Load, simplify, and add geoJSON coordinates.
            # Exclude geo if no or multiple renderings are present.
            if len(json_text) != 1:
                continue
            geojson = json.loads(json_text[0])
            geo_feature['geometry']['coordinates'] = (
                coerce_geojson_to_righthand_rule(geojson['coordinates'],
                                                 geojson['type']))
            features.append(geo_feature)
    return Response(json.dumps({
        "type": "FeatureCollection",
        "features": features,
        "properties": {
            "current_geo": place_dcid
        }
    }),
                    200,
                    mimetype='application/json')
Beispiel #4
0
def choropleth_values():
    """Returns data for geographic subregions for a certain statistical
            variable.

    API Params:
        geoDcid: The currently viewed geography to render, as a string.
        level: The subgeographic level to pull and display information for,
                as a string. Choices: Country, AdministrativeLevel[1/2], City.
        statVar: The statistical variable to pull data about.

    API Returns:
        values: dictionary of geo to statistical variable values (as a float)
            for all subgeos.
    """
    # Get required request parameters.
    requested_geoDcid = flask.request.args.get("geoDcid")
    if not requested_geoDcid:
        return flask.jsonify({"error": "Must provide a 'geoDcid' field!"}, 400)
    stat_var = flask.request.args.get("statVar")
    if not stat_var:
        return flask.jsonify({"error": "Must provide a 'statVar' field!"}, 400)
    display_level = get_sublevel(requested_geoDcid,
                                 flask.request.args.get("level"))
    if not display_level:
        return flask.jsonify(
            {
                "error":
                f"Failed to automatically resolve geographic subdivision level for"
                +
                f"{requested_geoDcid}. Please provide a 'level' field manually."
            }, 400)

    # Get all subgeos.
    geos_contained_in_place = dc.get_places_in([requested_geoDcid],
                                               display_level).get(
                                                   requested_geoDcid, [])
    values_by_geo = dc.get_stats(geos_contained_in_place, stat_var)

    # Add to dictionary for response.
    populations_by_geo = {}
    for geo_id, payload in values_by_geo.items():
        if payload and "data" in payload:
            latest_data = get_latest_data(payload)
            if latest_data:
                populations_by_geo[geo_id] = latest_data

    # Return as json payload.
    return flask.jsonify(populations_by_geo, 200)
Beispiel #5
0
def get_map_points():
    """
    Get map point data for the given place type enclosed within the given dcid
    """
    place_dcid = request.args.get("placeDcid")
    if not place_dcid:
        return Response(json.dumps("error: must provide a placeDcid field"),
                        400,
                        mimetype='application/json')
    place_type = request.args.get("placeType")
    if not place_type:
        return Response(json.dumps("error: must provide a placeType field"),
                        400,
                        mimetype='application/json')
    geos = []
    geos = dc_service.get_places_in([place_dcid],
                                    place_type).get(place_dcid, [])
    if not geos:
        return Response(json.dumps({}), 200, mimetype='application/json')
    names_by_geo = place_api.get_display_name('^'.join(geos), g.locale)
    latitude_by_geo = dc_service.get_property_values(geos, "latitude")
    longitude_by_geo = dc_service.get_property_values(geos, "longitude")

    map_points_list = []
    for geo_id, latitude in latitude_by_geo.items():
        longitude = longitude_by_geo.get(geo_id, [])
        if len(latitude_by_geo) == 0 or len(longitude) == 0:
            continue
        map_point = {
            "placeDcid": geo_id,
            "placeName": names_by_geo.get(geo_id, "Unnamed Place"),
            "latitude": float(latitude[0]),
            "longitude": float(longitude[0])
        }
        map_points_list.append(map_point)
    return Response(json.dumps(map_points_list),
                    200,
                    mimetype='application/json')
Beispiel #6
0
def choropleth_geo():
    """Returns data for geographic subregions for a certain statistical
            variable.

    API Params:
        geoDcid: The currently viewed geography to render, as a string.
        level: The subgeographic level to pull and display information for,
                as a string. Choices: Country, AdministrativeLevel[1/2], City.
        mdom: The measurement denominator to use as a string.
            Defaults to "Count_Person".


    API Returns:
        geoJson: geoJson format that includes statistical variables info,
            geoDcid, and name for all subregions.
    """
    # Get required request parameters.
    requested_geoDcid = flask.request.args.get("geoDcid")
    if not requested_geoDcid:
        return flask.jsonify({"error": "Must provide a 'geoDcid' field!"}, 400)
    display_level = get_sublevel(requested_geoDcid,
                                 flask.request.args.get("level"))
    geo_json_prop = GEOJSON_PROPERTY_MAP.get(display_level, None)
    if not display_level:
        return flask.jsonify(
            {
                "error":
                f"Failed to automatically resolve geographic subdivision level for"
                +
                f"{requested_geoDcid}. Please provide a 'level' field manually."
            }, 400)
    if not geo_json_prop:
        return flask.jsonify(
            {
                "error":
                f"Geojson data is not available for the geographic subdivision"
                + f"level needed for {requested_geoDcid}."
            }, 400)
    # Get optional fields.
    measurement_denominator = flask.request.args.get("mdom",
                                                     default="Count_Person")

    # Get list of all contained places.
    geos_contained_in_place = dc.get_places_in([requested_geoDcid],
                                               display_level).get(
                                                   requested_geoDcid, [])

    # Download statistical variable, names, and geojson for subgeos.
    # Also, handle the case where only a fraction of values are returned.
    names_by_geo = dc.get_property_values(
        geos_contained_in_place + [requested_geoDcid], "name")
    geojson_by_geo = dc.get_property_values(geos_contained_in_place,
                                            geo_json_prop)

    # Download population data if per capita.
    # TODO(iancostello): Determine how to handle populations
    # and statistical values from different times.
    population_by_geo = dc.get_stats(geos_contained_in_place,
                                     measurement_denominator)

    # Process into a combined json object.
    features = []
    for geo_id, json_text in geojson_by_geo.items():
        # Valid response needs at least geometry and a name.
        if not json_text:
            continue
        if geo_id not in names_by_geo:
            continue
        geo_feature = {
            "type": "Feature",
            "geometry": {
                "type": "MultiPolygon",
            },
            "id": geo_id,
            "properties": {
                # Choose the first name when multiple are present.
                "name": names_by_geo.get(geo_id, ["Unnamed Area"])[0],
                "hasSublevel": (display_level in LEVEL_MAP),
                "geoDcid": geo_id,
            }
        }

        # Load, simplify, and add geoJSON coordinates.
        # Exclude geo if no or multiple renderings are present.
        if len(json_text) != 1:
            continue

        geojson = json.loads(json_text[0])
        geo_feature['geometry']['coordinates'] = (
            coerce_geojson_to_righthand_rule(geojson['coordinates'],
                                             geojson['type']))

        if geo_id not in population_by_geo:
            continue

        population_payload = population_by_geo[geo_id]
        data = get_data(population_payload)

        # TODO(edumorales): return the population
        # for N date that all places have in common.
        if data:
            max_date = max(data)
            geo_feature["properties"]["pop"] = data[max_date]

        # Add to main dataframe.
        features.append(geo_feature)

    # Return as json payload.
    return flask.jsonify(
        {
            "type": "FeatureCollection",
            "features": features,
            "properties": {
                "current_geo":
                names_by_geo.get(requested_geoDcid, ["Unnamed Area"])[0]
            }
        }, 200)
Beispiel #7
0
def choropleth_geo():
    """Returns data for geographic subregions for a certain statistical 
            variable.

    API Params:
        geoDcid: The currently viewed geography to render, as a string.
        level: The subgeographic level to pull and display information for,
                as a string. Choices: Country, AdministrativeLevel[1/2], City.
        mdom: The measurement denominator to use as a string.
            Defaults to "Count_Person".


    API Returns:
        geoJson: geoJson format that includes statistical variables info,
            geoDcid, and name for all subregions.
    """
    # Get required request parameters.
    requested_geoDcid = flask.request.args.get("geoDcid")
    if not requested_geoDcid:
        return flask.jsonify({"error": "Must provide a 'geoDcid' field!"}, 400)
    display_level = get_sublevel(requested_geoDcid,
                                 flask.request.args.get("level"))

    # Get optional fields.
    measurement_denominator = flask.request.args.get("mdom",
                                                     default="Count_Person")

    # Get list of all contained places.
    # TODO(iancostello): Handle a failing function call.
    geos_contained_in_place = dc.get_places_in(
        [requested_geoDcid], display_level)[requested_geoDcid]

    # Download statistical variable, names, and geojson for subgeos.
    # TODO(iancostello): Handle failing function calls.
    # Also, handle the case where only a fraction of values are returned.
    names_by_geo = dc.get_property_values(
        geos_contained_in_place + [requested_geoDcid], "name")
    geojson_by_geo = dc.get_property_values(geos_contained_in_place,
                                            "geoJsonCoordinates")

    # Download population data if per capita.
    # TODO(iancostello): Determine how to handle populations
    # and statistical values from different times.
    population_by_geo = dc.get_stats(geos_contained_in_place,
                                     measurement_denominator)

    # Process into a combined json object.
    features, values = [], []
    for geo_id, json_text in geojson_by_geo.items():
        # Valid response needs at least geometry and a name.
        if json_text and geo_id in names_by_geo:
            geo_feature = {
                "type": "Feature",
                "geometry": {
                    "type": "MultiPolygon",
                },
                "id": geo_id,
                "properties": {
                    # Choose the first name when multiple are present.
                    # TODO(iancostello): Implement a better approach.
                    "name": names_by_geo[geo_id][0],
                    "hasSublevel": (display_level in LEVEL_MAP),
                    "geoDcid": geo_id,
                }
            }
            # Load, simplify, and add geoJSON coordinates.
            # First returned value is chosen always.
            # TODO(iancostello): Implement a better approach.
            geojson = json.loads(json_text[0])
            geo_feature['geometry']['coordinates'] = (
                coerce_geojson_to_righthand_rule(geojson['coordinates'],
                                                 geojson['type']))
            # Process Statistical Observation if valid.
            if ('data' in population_by_geo.get(geo_id, [])):
                # Grab the latest available data.
                geo_feature["properties"]["pop"] = next(
                    iter(reversed(population_by_geo[geo_id]['data'].values())))

            # Add to main dataframe.
            features.append(geo_feature)

    # Return as json payload.
    return flask.jsonify(
        {
            "type": "FeatureCollection",
            "features": features,
            "properties": {
                # TODO(iancostello): Don't just pick the first and check.
                "current_geo": names_by_geo[requested_geoDcid][0]
            }
        },
        200)