|
43 | 43 | "\n",
|
44 | 44 | "The following packages are good to know, but not required, as we will be walking through them below.\n",
|
45 | 45 | "\n",
|
| 46 | + "\n", |
46 | 47 | "| Concepts | Importance | Notes |\n",
|
47 |
| - "| --- | --- | --- |\n", |
48 |
| - "| [Intro to GeoViews](https://geoviews.org/) | Helpful | |\n", |
49 |
| - "| [Intro to Geopandas](https://geopandas.org/) | Helpful | |\n", |
50 |
| - "| [Intro to Panel](https://panel.holoviz.org/) | Helpful | |\n", |
51 |
| - "| [Intro to Requests](https://requests.readthedocs.io/en/latest/) | Helpful | |\n", |
52 |
| - "| [Intro to Cartopy](https://foundations.projectpythia.org/core/cartopy/cartopy.html) | Helpful | |\n", |
| 48 | + "| --| --| --- |\n", |
| 49 | + "| [Intro to hvPlot](https://hvplot.holoviz.org/) | Helpful | High-level plotting |\n", |
| 50 | + "| [Intro to GeoViews](https://geoviews.org/) | Helpful | Geographic visualizations |\n", |
| 51 | + "| [Intro to Geopandas](https://geopandas.org/) | Helpful | GIS files |\n", |
| 52 | + "| [Intro to Panel](https://panel.holoviz.org/) | Helpful | Dashboard creations |\n", |
| 53 | + "| [Intro to Requests](https://requests.readthedocs.io/en/latest/) | Helpful | Webpage requests |\n", |
| 54 | + "| [Intro to Cartopy](https://foundations.projectpythia.org/core/cartopy/cartopy.html) | Helpful | Map projections |\n", |
| 55 | + "| [Intro to OWSLib](https://owslib.readthedocs.io/en/latest/usage.html) | Helpful | WFS URLs |\n", |
53 | 56 | "\n",
|
54 | 57 | "- **Time to learn**: 10 minutes\n",
|
55 | 58 | "\n",
|
|
77 | 80 | "\n",
|
78 | 81 | "Geopandas is a Python library used for working with geospatial data. With Geopandas, you can easily load shapefiles into a GeoDataFrame, which is a tabular data structure that combines the spatial information of the shapefile with its attribute data. This allows you to perform various geospatial operations and analyses on the data.\n",
|
79 | 82 | "\n",
|
| 83 | + "OWSLib is a Python library designed for client-side programming using the interface standards of the Open Geospatial Consortium (OGC) web services and their associated content models. Specifically, in this scenario, OWSLib will be utilized solely for the purpose of constructing URLs for WFS.\n", |
| 84 | + "\n", |
80 | 85 | "To enable the Bokeh plotting backend for GeoViews, we use the line `gv.extension(\"bokeh\")`. GeoViews supports multiple plotting backends, including Bokeh and Matplotlib, giving you the flexibility to choose the one that suits your requirements best.\n",
|
81 | 86 | "\n",
|
82 | 87 | "Lastly, `pn.extension()` initializes the Panel library and sets up the necessary environment for creating interactive panels and dashboards. You can also specify configurations such as `sizing_mode=\"stretch_width\"` within `pn.extension()`."
|
|
94 | 99 | "import panel as pn\n",
|
95 | 100 | "import requests\n",
|
96 | 101 | "import geopandas as gpd\n",
|
| 102 | + "from owslib.wfs import WebFeatureService\n", |
97 | 103 | "\n",
|
98 | 104 | "gv.extension(\"bokeh\")\n",
|
99 | 105 | "pn.extension()"
|
|
104 | 110 | "cell_type": "markdown",
|
105 | 111 | "metadata": {},
|
106 | 112 | "source": [
|
107 |
| - "## Accessing WFS\n", |
| 113 | + "## Accessing WFS Features\n", |
108 | 114 | "\n",
|
109 | 115 | "Unlike WMS, where built-in sources exist, accessing WFS requires utilizing external sources. However, similar to WMS, there are several external sources available for WFS, such as NOAA, which can be easily accessed. Depending on the specific source, accessing WFS data may also be free of charge.\n",
|
110 | 116 | "\n",
|
111 |
| - "For instance, if you prefer to obtain the CPC 6-10 day outlook as a GeoJSON instead of a tile image, you can utilize the gpd.read_file function, providing the relevant URL and specific parameters for the external tile service." |
| 117 | + "For instance, if you prefer to obtain the CPC 6-10 day outlook as a GeoJSON instead of a tile image, you can utilize the gpd.read_file function, providing the relevant URL, or building it with `OWSLib`." |
112 | 118 | ]
|
113 | 119 | },
|
114 | 120 | {
|
|
117 | 123 | "metadata": {},
|
118 | 124 | "outputs": [],
|
119 | 125 | "source": [
|
120 |
| - "outlook_url = \"\"\"\n", |
121 |
| - "https://idpgis.ncep.noaa.gov/arcgis/services/NWS_Climate_Outlooks/cpc_6_10_day_outlk/MapServer/WFSServer?\n", |
122 |
| - "service=WFS&\n", |
123 |
| - "version=2.0.0&\n", |
124 |
| - "request=GetFeature&\n", |
125 |
| - "typeName=cpc_6_10_day_outlk:CPC_6-10_Day_Temperature_Outlook&\n", |
126 |
| - "outputFormat=GEOJSON\n", |
127 |
| - "\"\"\".replace(\"\\n\", \"\")\n", |
128 |
| - "\n", |
129 |
| - "gdf = gpd.read_file(outlook_url)\n", |
| 126 | + "def get_wfs_feature(base_resource_url):\n", |
| 127 | + " wfs = WebFeatureService(base_resource_url, version=\"2.0.0\")\n", |
| 128 | + " wfs_contents = wfs.contents\n", |
| 129 | + " wfs_layer = list(wfs_contents)[0]\n", |
| 130 | + " feature = wfs.getfeature(\n", |
| 131 | + " typename=[wfs_layer],\n", |
| 132 | + " outputFormat=\"GeoJson\",\n", |
| 133 | + " )\n", |
| 134 | + " return feature\n", |
| 135 | + "\n", |
| 136 | + "base_resource_url = \"https://mapservices.weather.noaa.gov/vector/services/outlooks/cpc_6_10_day_outlk/MapServer/WFSServer?service=WFS&version=1.0.0&request=GetFeature&srsname=EPSG%3A4326&typename=cpc_6_10_day_outlk%3ACPC_6-10_Day_Temperature_Outlook&propertyname=%2A\"\n", |
| 137 | + "feature = get_wfs_feature(base_resource_url)\n", |
| 138 | + "gdf = gpd.read_file(feature)\n", |
130 | 139 | "gdf.head()"
|
131 | 140 | ]
|
132 | 141 | },
|
|
176 | 185 | "temp_outlook"
|
177 | 186 | ]
|
178 | 187 | },
|
179 |
| - { |
180 |
| - "attachments": {}, |
181 |
| - "cell_type": "markdown", |
182 |
| - "metadata": {}, |
183 |
| - "source": [ |
184 |
| - "## Building WFS URLs\n", |
185 |
| - "\n", |
186 |
| - "You might also be wondering where that URL came from.\n", |
187 |
| - "\n", |
188 |
| - "Admittedly, it's not very straightforward, but by following these instructions, it becomes quite simple!\n", |
189 |
| - "\n", |
190 |
| - "1. Begin by searching \"NOAA WFS\" on Google.\n", |
191 |
| - "2. Locate and click on the first search result, which is \"[WebServices](https://www.weather.gov/gis/WebServices)\".\n", |
192 |
| - "3. Choose a specific link, such as \"[6 to 10 Day Precipitation and Temperature Outlooks](https://idpgis.ncep.noaa.gov/arcgis/rest/services/NWS_Climate_Outlooks/cpc_6_10_day_outlk/MapServer)\".\n", |
193 |
| - "4. On the top left of the page, click on the small text labeled \"[WFS](https://idpgis.ncep.noaa.gov/arcgis/services/NWS_Climate_Outlooks/cpc_6_10_day_outlk/MapServer/WFSServer?request=GetCapabilities&service=WFS)\".\n", |
194 |
| - "5. Remove the section \"request=GetCapabilities&\" from the URL.\n", |
195 |
| - "6. Copy the XML from get capabilities and **paste it alongside** the following prompt for ChatGPT: \"Create a valid WFS URL; be sure the output format is GEOJSON. https://idpgis.ncep.noaa.gov/arcgis/services/NWS_Climate_Outlooks/cpc_6_10_day_outlk/MapServer/WFSServer?service=WFS {xml_contents}\".\n", |
196 |
| - "7. By following these steps, you will obtain a URL that is ready to be fetched with `gpd.read_file`!" |
197 |
| - ] |
198 |
| - }, |
199 |
| - { |
200 |
| - "attachments": {}, |
201 |
| - "cell_type": "markdown", |
202 |
| - "metadata": {}, |
203 |
| - "source": [ |
204 |
| - "## Troubleshooting URLs\n", |
205 |
| - "\n", |
206 |
| - "If geopandas is unable to access the URL, try `requests` and see the contents.\n", |
207 |
| - "\n", |
208 |
| - "Here's an example of a URL using an incorrect type name." |
209 |
| - ] |
210 |
| - }, |
211 |
| - { |
212 |
| - "cell_type": "code", |
213 |
| - "execution_count": null, |
214 |
| - "metadata": {}, |
215 |
| - "outputs": [], |
216 |
| - "source": [ |
217 |
| - "url = \"\"\"\n", |
218 |
| - "https://idpgis.ncep.noaa.gov/arcgis/services/NWS/USGS_Stream_Gauges/MapServer/WFSServer?\n", |
219 |
| - "service=WFS&\n", |
220 |
| - "request=GetFeature&\n", |
221 |
| - "typeNames=USGS_Stream_Gauges&\n", |
222 |
| - "outputFormat=GEOJSON\n", |
223 |
| - "\"\"\"\n", |
224 |
| - "\n", |
225 |
| - "resp = requests.get(url)\n", |
226 |
| - "print(resp.text)" |
227 |
| - ] |
228 |
| - }, |
229 |
| - { |
230 |
| - "attachments": {}, |
231 |
| - "cell_type": "markdown", |
232 |
| - "metadata": {}, |
233 |
| - "source": [ |
234 |
| - "To address this issue, you can either review the `GetCapabilities` documentation or simply input the URL along with the error into ChatGPT (the latter is usually easier).\n", |
235 |
| - "\n", |
236 |
| - "Here's the revised URL; notice the response is no longer the same." |
237 |
| - ] |
238 |
| - }, |
239 |
| - { |
240 |
| - "cell_type": "code", |
241 |
| - "execution_count": null, |
242 |
| - "metadata": {}, |
243 |
| - "outputs": [], |
244 |
| - "source": [ |
245 |
| - "url = \"\"\"\n", |
246 |
| - "https://idpgis.ncep.noaa.gov/arcgis/services/NWS/USGS_Stream_Gauges/MapServer/WFSServer?\n", |
247 |
| - "service=WFS&\n", |
248 |
| - "request=GetFeature&\n", |
249 |
| - "typeNames=USGS_Stream_Gauges:Gauge_Locations&\n", |
250 |
| - "outputFormat=GEOJSON\n", |
251 |
| - "\"\"\"\n", |
252 |
| - "\n", |
253 |
| - "resp = requests.get(url)\n", |
254 |
| - "resp.text.splitlines()[:2]" |
255 |
| - ] |
256 |
| - }, |
257 |
| - { |
258 |
| - "attachments": {}, |
259 |
| - "cell_type": "markdown", |
260 |
| - "metadata": {}, |
261 |
| - "source": [ |
262 |
| - "## Filtering Properties\n", |
263 |
| - "\n", |
264 |
| - "Sometimes, all you need is a piece of the data--it'd be inefficient to load the entire the dataset in that case.\n", |
265 |
| - "\n", |
266 |
| - "Good news is that it's possible to filter by properties. Bad news is that the syntax is pretty difficult... without the aid of ChatGPT, that is!\n", |
267 |
| - "\n", |
268 |
| - "With ChatGPT, simply paste the URL you want to filter and comment that you want to filter by site_name \"SPRAGUE CREEK NEAR SPRAGUE, MANITOBA, CANADA\" as an example." |
269 |
| - ] |
270 |
| - }, |
271 |
| - { |
272 |
| - "cell_type": "code", |
273 |
| - "execution_count": null, |
274 |
| - "metadata": {}, |
275 |
| - "outputs": [], |
276 |
| - "source": [ |
277 |
| - "url = \"\"\"\n", |
278 |
| - "https://idpgis.ncep.noaa.gov/arcgis/services/NWS/USGS_Stream_Gauges/MapServer/WFSServer?\n", |
279 |
| - "service=WFS&\n", |
280 |
| - "request=GetFeature&\n", |
281 |
| - "typeNames=USGS_Stream_Gauges:Gauge_Locations&\n", |
282 |
| - "outputFormat=GEOJSON&\n", |
283 |
| - "Filter=<Filter><PropertyIsEqualTo><PropertyName>site_name</PropertyName><Literal>SPRAGUE CREEK NEAR SPRAGUE, MANITOBA, CANADA</Literal></PropertyIsEqualTo></Filter>\n", |
284 |
| - "\"\"\"\n", |
285 |
| - "\n", |
286 |
| - "resp = requests.get(url)\n", |
287 |
| - "gdf = gpd.read_file(resp.text)\n", |
288 |
| - "gdf" |
289 |
| - ] |
290 |
| - }, |
291 | 188 | {
|
292 | 189 | "attachments": {},
|
293 | 190 | "cell_type": "markdown",
|
|
0 commit comments