{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Landmarks (using Local Files)\n", "\n", "Four scores are here computed to extract computation landmarks: structural, visual, cultural, pragmatic.\n", "The user has to provide at least an attribute shapefile containing the height attribute (the only mandatory field).\n", "See below for further instructions on the data sources.\n", "\n", "The user can also provide, seaprately, information about cultural/historical landmarks (e.g. position of listed important buildings); see *3 - Cultural component*" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import sys\n", "from pathlib import Path\n", "\n", "repo_root = Path.cwd().resolve().parents[1]\n", "sys.path.insert(0, str(repo_root))\n", "\n", "import cityImage as ci\n", "import matplotlib.pyplot as plt\n", "import matplotlib.font_manager as fm\n", "\n", "fm.fontManager.addfont(\"times.ttf\")\n", "plt.rcParams[\"font.family\"] = \"Times New Roman\"" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "import pandas as pd, numpy as np, geopandas as gpd, osmnx as ox\n", "from shapely.geometry import LineString\n", "%matplotlib inline\n", "\n", "import warnings\n", "warnings.simplefilter(action=\"ignore\")\n", "pd.options.display.float_format = '{:20.2f}'.format\n", "pd.options.mode.chained_assignment = None\n", "\n", "import osmnx as ox\n", "import cityImage as ci" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Functions from the cityImage API used in this notebook\n", "\n", "This notebook scores buildings as candidate *landmarks*, combining structural, visual, cultural and pragmatic salience into global and local imageability scores, starting from locally provided files.\n", "\n", "- [buildings_from_file](../api/cityImage.buildings_from_file.rst#cityImage.buildings_from_file) \u2014 Load building footprints from a local file into the buildings schema.\n", "- [buildings_from_osm](../api/cityImage.buildings_from_osm.rst#cityImage.buildings_from_osm) \u2014 Download building footprints from OpenStreetMap into the cityImage buildings schema.\n", "- [select_buildings_by_study_area](../api/cityImage.select_buildings_by_study_area.rst#cityImage.select_buildings_by_study_area) \u2014 Restrict a buildings GeoDataFrame to those within the case-study area.\n", "- [plot_gdf](../api/cityImage.plot_gdf.rst#cityImage.plot_gdf) \u2014 Plot a single GeoDataFrame with cityImage styling.\n", "- [structural_score](../api/cityImage.structural_score.rst#cityImage.structural_score) \u2014 Compute the structural landmark component (distance from the network, 2D advance visibility, number of neighbours).\n", "- [compute_3d_sight_lines](../api/cityImage.compute_3d_sight_lines.rst#cityImage.compute_3d_sight_lines) \u2014 Compute 3D sight lines between viewpoints and buildings for visibility scoring.\n", "- [visibility_score](../api/cityImage.visibility_score.rst#cityImage.visibility_score) \u2014 Compute the visibility landmark component (facade area and, when available, 3D sight-line visibility).\n", "- [cultural_score](../api/cityImage.cultural_score.rst#cityImage.cultural_score) \u2014 Compute the cultural landmark component from historical/cultural attributes.\n", "- [classify_land_use](../api/cityImage.classify_land_use.rst#cityImage.classify_land_use) \u2014 Classify raw land-use values into cityImage land-use categories.\n", "- [pragmatic_score](../api/cityImage.pragmatic_score.rst#cityImage.pragmatic_score) \u2014 Compute the pragmatic landmark component from land-use frequency.\n", "- [compute_global_scores](../api/cityImage.compute_global_scores.rst#cityImage.compute_global_scores) \u2014 Combine the component scores into a global (city-wide) landmark score.\n", "- [compute_local_scores](../api/cityImage.compute_local_scores.rst#cityImage.compute_local_scores) \u2014 Combine the component scores into a local (neighbourhood-relative) landmark score.\n", "- [plot_grid_gdf_columns](../api/cityImage.plot_grid_gdf_columns.rst#cityImage.plot_grid_gdf_columns) \u2014 Plot several columns of one GeoDataFrame as a grid of maps.\n", "\n", "For the full list of functions and their parameters, see the [API reference](../api.rst)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Loading the Layers\n", "\n", "Proive the input path where the building layer of the city/area of interest is stored. This file shouldn't be exactly one's case-study area but a larger area (e.g. not Boston's city centre (case-study area), but the entire city of Boston).\n", "\n", "Landmarks are extracted from buildings that are within the chosen case-study area (`buildings` geodataframe). Other buildings around the case study area have to be included in the analysis in order to correctly compute the landmark measures, namely, to avoid that buildings located along the boundaries of the case-study area benefit from the fact that in the `buildings` geodataframe outer buildings are not included.\n", "\n", "In other words, `buildings` is a subset of `obstructions`.\n", "\n", "One can set `option` to:\n", "\n", "* `1` - Load one's `buildings` and `obstructions` gdfs from a local path. Choose when: Files are ready and contains the `height`, and a `land_use` fields.\n", "\n", "* `2` - Get a large set of building from OSM (`obstructions`), extract the `buildings`, and attach information regarding `height` and `land_use` use from an existing file that it is in one's possession (if this file is for example issued by an official data source and therefore more complete than OSM). This step makes sense only if one's not comfortable with the shapes of the building footprints in their possession. Often these files contain footprints too precise and not fitting the purposes of this work. Instead, OSM provides already simplified footprints. Choose when: Having a shapefile with the `height` and `land_use` attributes but it is preferred to use the more simplified OSM geometries and attach the attributes to the OSM footprints.\n", "\n", "* `3` - Load a buildings dataframe that would correspond to `obstructions` and obtain the `buildings` geodataframe by using a certain distance or a case-study area polygon (see below for details). Choose when: Having a shapefile containing buildings in a large area, including and outside one's case study area; one is comfortable with the geometries and the `height` and `land_use` attributes are contained in such a file.\n", "\n", "Buildings smaller than 200 square meters are kept out of the analysis, as well as buildings whose height is lower than 1mt (when the `height` field is filled).\n", "\n", "In general, the obstructions geodataframe should include buildings located around 1000 m away from the boundary of the buildings gdf. This may vary, of course, on the basis of the analysis that one intends to conduct.\n", "\n", "**Ininitialising path, names, etc.**" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "city_name = 'Boston'\n", "epsg = 26986 \n", "crs = 'EPSG:'+str(epsg)\n", "input_path = '../data/'+city_name+\"/\"\n", "option = 3" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "# from local\n", "case_study_area = gpd.read_file(input_path+city_name+'_studyArea.gpkg').to_crs(epsg=epsg).iloc[0].geometry" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "#specify the land use field's name in the file\n", "height_field = 'height' \n", "base_field = 'base'\n", "land_use_field = 'landUse'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Defining the Layers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Option 1\n", "\n", "Using already prepared `buildings` and `obstructions` datasets. One should be aware that `buildings` should be a perfect subset of `obstructions`. This means that all the buildings in `buildings` should be in `obstructions`, sharing the same attributes.\n", "\n", "* The datasets should contain fields named `base` and `height`, representing the base height of the building (could be set to 0) and the height of the building.\n", "* The `buildingID` should be already set correctly, that is, matching in the `obstructions`and `buildings` GeoDataFrames." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "if option == 1:\n", " buildings = gpd.read_file(input_path+city_name+'_buildings.shp').to_crs(epsg=epsg)\n", " obstructions = gpd.read_file(input_path+city_name+'_obstructions.shp').to_crs(epsg=epsg)\n", " buildings.index, obstructions.index = buildings.buildingID, obstructions.buildingID\n", " buildings.index.name, obstructions.index.name = None, None\n", " obstructions['land_uses_raw'] = obstructions[land_use_field]\n", " obstructions.drop(land_use_field, axis = 1, inplace = True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Option 2\n", "\n", "Download the `obstructions` from OpenStreetMap for a large metropolitan area that covers your case-study area, and (optionally) enrich them with `height`, `base` and land-use attributes taken from a local *detailed-buildings* file. The `buildings` GeoDataFrame is then obtained by restricting the obstructions to the actual case-study polygon.\n", "\n", "This path uses [buildings_from_osm](../api/cityImage.buildings_from_osm.rst#cityImage.buildings_from_osm), [assign_building_heights_from_other_gdf](../api/cityImage.assign_building_heights_from_other_gdf.rst#cityImage.assign_building_heights_from_other_gdf), [land_use_from_other_gdf](../api/cityImage.land_use_from_other_gdf.rst#cityImage.land_use_from_other_gdf) and [select_buildings_by_study_area](../api/cityImage.select_buildings_by_study_area.rst#cityImage.select_buildings_by_study_area)." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "# a large metropolitan area that fully covers your case-study area\n", "place = '' # e.g. \"Boston, Massachusetts\"\n", "if option == 2:\n", " obstructions = ci.buildings_from_osm(place, download_method=\"OSMplace\", crs=crs)\n", "\n", " # (optional) attach height/base and land-use attributes from a local detailed-buildings file\n", " attribute_file = gpd.read_file(input_path + city_name + '_detailedBuildings.gpkg').to_crs(epsg=epsg)\n", " obstructions = ci.assign_building_heights_from_other_gdf(\n", " obstructions, attribute_file, crs, base_field=base_field, height_field=height_field,\n", " )\n", " obstructions = ci.land_use_from_other_gdf(\n", " obstructions, attribute_file,\n", " new_land_use_column=\"land_uses_raw\", other_land_use_column=land_use_field,\n", " )\n", " # NB: cityImage no longer ships a footprint-simplification helper; if your OSM\n", " # footprints are very detailed, simplify them with GeoPandas' geometry.simplify(...).\n", "\n", " # restrict the obstructions to the actual case-study area\n", " buildings = ci.select_buildings_by_study_area(\n", " obstructions, method=\"polygon\", polygon=case_study_area,\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Option 3\n", "\n", "If one doesn't have a precise case-study area polygon to pass: Use the parameter `distance_from_center` to indicate till how far away from the center of the case-study area one wants to include buildings. When the user doesn't provide the case-study area polygon nor the distance parameter, `buildings` and `obstructions` overlap; they have the same extent as the original input file.\n", "\n", "`height_field` and `base_field` should indicate the maximum and the base elevations attributes of the buildings, in the original .shp file. When `base_field` is not provided, `base` is automatically set to 0. `height_field` is necessary to perform the landmark extraction. If such a feature is not at disposal, one should set `height_field` to `None`." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "if option == 3:\n", " obstructions = ci.buildings_from_file(\n", " input_path+city_name+ '_obstructions.gpkg',\n", " crs,\n", " case_study_area=None,\n", " distance_from_center=None,\n", " height_field=height_field,\n", " base_field=base_field,\n", " )\n", " buildings = ci.select_buildings_by_study_area(\n", " obstructions,\n", " method=\"polygon\",\n", " polygon=case_study_area,\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Optional readjustments**" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "obstructions = obstructions[obstructions.geometry.within(buildings.geometry.unary_union.convex_hull.buffer(1000))]" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
| \n", " | height | \n", "base | \n", "area | \n", "landUse | \n", "geometry | \n", "buildingID | \n", "
|---|---|---|---|---|---|---|
| 151 | \n", "19.98 | \n", "1.55 | \n", "935.45 | \n", "Residential Condo Unit | \n", "POLYGON ((237051.689 902972.866, 236990.897 90... | \n", "151 | \n", "
| 550 | \n", "23.53 | \n", "1.72 | \n", "2466.09 | \n", "Commercial Condo Unit | \n", "POLYGON ((237109.75 901488.558, 237000.835 901... | \n", "550 | \n", "
| 551 | \n", "11.75 | \n", "-1.29 | \n", "526.52 | \n", "Commercial | \n", "POLYGON ((237027.023 901476.82, 237001.853 901... | \n", "551 | \n", "
| 552 | \n", "7.21 | \n", "-0.38 | \n", "292.29 | \n", "Commercial Land | \n", "POLYGON ((237044.307 901416.818, 237004.172 90... | \n", "552 | \n", "
| 553 | \n", "20.49 | \n", "1.39 | \n", "2620.96 | \n", "Residential Condo Unit | \n", "POLYGON ((237142.368 901348.946, 237005.398 90... | \n", "553 | \n", "
| ... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "
| 6558 | \n", "25.68 | \n", "1.52 | \n", "4086.12 | \n", "Exempt | \n", "POLYGON ((232808.536 899562.8, 232828.761 8995... | \n", "6558 | \n", "
| 6559 | \n", "16.56 | \n", "1.95 | \n", "1594.13 | \n", "Exempt | \n", "POLYGON ((232822.026 899506.197, 232834.73 899... | \n", "6559 | \n", "
| 6571 | \n", "16.93 | \n", "1.41 | \n", "2392.47 | \n", "Commercial Land | \n", "POLYGON ((232887.683 899608.367, 232906.511 89... | \n", "6571 | \n", "
| 6572 | \n", "27.70 | \n", "1.17 | \n", "3773.17 | \n", "Commercial | \n", "POLYGON ((232814.013 899628.112, 232865.439 89... | \n", "6572 | \n", "
| 6573 | \n", "10.37 | \n", "0.95 | \n", "743.39 | \n", "Commercial | \n", "POLYGON ((232810.102 899634.308, 232858.034 89... | \n", "6573 | \n", "
2943 rows \u00d7 6 columns
\n", "| \n", " | height | \n", "base | \n", "area | \n", "landUse | \n", "geometry | \n", "buildingID | \n", "road | \n", "2dvis | \n", "neigh | \n", "
|---|---|---|---|---|---|---|---|---|---|
| 550 | \n", "23.53 | \n", "1.72 | \n", "2466.09 | \n", "Commercial Condo Unit | \n", "POLYGON ((237109.75 901488.558, 237000.835 901... | \n", "550 | \n", "17.99 | \n", "89352.63 | \n", "20 | \n", "
| 551 | \n", "11.75 | \n", "-1.29 | \n", "526.52 | \n", "Commercial | \n", "POLYGON ((237027.023 901476.82, 237001.853 901... | \n", "551 | \n", "17.84 | \n", "38375.87 | \n", "17 | \n", "
| 552 | \n", "7.21 | \n", "-0.38 | \n", "292.29 | \n", "Commercial Land | \n", "POLYGON ((237044.307 901416.818, 237004.172 90... | \n", "552 | \n", "4.45 | \n", "41101.49 | \n", "16 | \n", "
| 553 | \n", "20.49 | \n", "1.39 | \n", "2620.96 | \n", "Residential Condo Unit | \n", "POLYGON ((237142.368 901348.946, 237005.398 90... | \n", "553 | \n", "4.11 | \n", "128536.86 | \n", "15 | \n", "
| 554 | \n", "12.17 | \n", "1.15 | \n", "793.82 | \n", "Commercial | \n", "POLYGON ((237199.594 901363.43, 237197.299 901... | \n", "554 | \n", "27.32 | \n", "183974.08 | \n", "7 | \n", "
| ... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "
| 6047 | \n", "17.80 | \n", "0.87 | \n", "589.50 | \n", "Residential Condo Unit | \n", "POLYGON ((236886.134 900440.682, 236853.291 90... | \n", "6047 | \n", "11.50 | \n", "77644.06 | \n", "13 | \n", "
| 6048 | \n", "7.47 | \n", "1.32 | \n", "351.20 | \n", "Residential Condo Unit | \n", "POLYGON ((236937.198 900463.676, 236930.845 90... | \n", "6048 | \n", "60.73 | \n", "58691.63 | \n", "8 | \n", "
| 6049 | \n", "23.56 | \n", "0.95 | \n", "3609.91 | \n", "None | \n", "POLYGON ((234702.34 900398.09, 234692.807 9004... | \n", "6049 | \n", "6.48 | \n", "19445.91 | \n", "21 | \n", "
| 6050 | \n", "71.55 | \n", "4.72 | \n", "4116.83 | \n", "None | \n", "POLYGON ((236414.03 900412.087, 236313.318 900... | \n", "6050 | \n", "4.47 | \n", "8520.39 | \n", "28 | \n", "
| 6051 | \n", "33.24 | \n", "0.77 | \n", "5753.09 | \n", "Commercial Condo Unit | \n", "POLYGON ((236873.645 900347.507, 236838.52 900... | \n", "6051 | \n", "9.76 | \n", "117504.70 | \n", "13 | \n", "
1408 rows \u00d7 9 columns
\n", "| \n", " | height | \n", "base | \n", "area | \n", "landUse | \n", "geometry | \n", "buildingID | \n", "road | \n", "2dvis | \n", "neigh | \n", "fac | \n", "3dvis | \n", "
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | \n", "23.53 | \n", "1.72 | \n", "2466.09 | \n", "Commercial Condo Unit | \n", "POLYGON ((237109.75 901488.558, 237000.835 901... | \n", "550 | \n", "17.99 | \n", "89352.63 | \n", "20 | \n", "1282.19 | \n", "0.38 | \n", "
| 1 | \n", "11.75 | \n", "1.00 | \n", "526.52 | \n", "Commercial | \n", "POLYGON ((237027.023 901476.82, 237001.853 901... | \n", "551 | \n", "17.84 | \n", "38375.87 | \n", "17 | \n", "325.96 | \n", "0.03 | \n", "
| 2 | \n", "7.21 | \n", "1.00 | \n", "292.29 | \n", "Commercial Land | \n", "POLYGON ((237044.307 901416.818, 237004.172 90... | \n", "552 | \n", "4.45 | \n", "41101.49 | \n", "16 | \n", "171.90 | \n", "0.38 | \n", "
| 3 | \n", "20.49 | \n", "1.39 | \n", "2620.96 | \n", "Residential Condo Unit | \n", "POLYGON ((237142.368 901348.946, 237005.398 90... | \n", "553 | \n", "4.11 | \n", "128536.86 | \n", "15 | \n", "1488.76 | \n", "0.57 | \n", "
| 4 | \n", "12.17 | \n", "1.15 | \n", "793.82 | \n", "Commercial | \n", "POLYGON ((237199.594 901363.43, 237197.299 901... | \n", "554 | \n", "27.32 | \n", "183974.08 | \n", "7 | \n", "382.03 | \n", "0.53 | \n", "