Created
August 4, 2021 18:43
-
-
Save datadavev/1741a78c88e7f3345a202190633cb022 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Dependencies:\n", | |
"\n", | |
"To run this notebook, pip install these in a virtual environment:\n", | |
"\n", | |
"- jupyter\n", | |
"- shapely\n", | |
"- requests\n", | |
"- jupyterlab-geojson\n", | |
"- ipyleaflet\n", | |
"\n", | |
"## Solr setup:\n", | |
"\n", | |
"Add the JTS jar. on my Max it went into: `/usr/local/Cellar/solr/8.9.0/server/solr-webapp/webapp/WEB-INF/lib`\n", | |
"\n", | |
"The examples here are using a collection called \"geo_test\". On my Mac, the solr config sets are located in `/usr/local/Cellar/solr/8.9.0/server/solr/configsets` In there, copy the `_default` config set to a new one called `spatial` that is used for testing here.\n", | |
"\n", | |
"Edit `spatial/conf/managed-schema` and ensure these exist therein:\n", | |
"```xml\n", | |
" <!--\n", | |
" LatLonPointSpatialField\n", | |
" https://solr.apache.org/guide/8_9/spatial-search.html#bboxfield\n", | |
" --> \n", | |
" <dynamicField name=\"*_ll\" type=\"location\" indexed=\"true\" stored=\"true\" />\n", | |
" <!-- \n", | |
" Bounding Box \n", | |
" https://solr.apache.org/guide/8_9/spatial-search.html#bboxfield\n", | |
" -->\n", | |
" <dynamicField name=\"*_bb\" type=\"bbox\" />\n", | |
" <!--\n", | |
" RptWithGeometryySpatialField\n", | |
" https://solr.apache.org/guide/8_9/spatial-search.html#rptwithgeometryspatialfield\n", | |
" -->\n", | |
" <dynamicField name=\"*_rpt\" type=\"location_rpt\" indexed=\"true\" stored=\"true\" multiValued=\"true\"/>\n", | |
"\n", | |
" <fieldType name=\"location\" class=\"solr.LatLonPointSpatialField\" docValues=\"true\"/>\n", | |
"\n", | |
" <fieldType name=\"bbox\" class=\"solr.BBoxField\"\n", | |
" geo=\"true\" distanceUnits=\"kilometers\" numberType=\"pdouble\" />\n", | |
"\n", | |
" <fieldType name=\"location_rpt\" class=\"solr.SpatialRecursivePrefixTreeFieldType\"\n", | |
" spatialContextFactory=\"JTS\"\n", | |
" geo=\"true\" \n", | |
" autoIndex=\"true\" \n", | |
" />\n", | |
"\n", | |
"```\n", | |
"\n", | |
"A collection using the `spatial` config set can be created / deleted from the command line, e.g.:\n", | |
"```\n", | |
"solr delete -c geo_test\n", | |
"brew services restart solr\n", | |
"solr create -d spatial -c geo_test\n", | |
"```\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import requests\n", | |
"import json\n", | |
"import logging\n", | |
"import shapely.wkt\n", | |
"import shapely.geometry\n", | |
"from IPython.display import GeoJSON\n", | |
"\n", | |
"logging.basicConfig(level=logging.DEBUG)\n", | |
"\n", | |
"DEFAULT_COLLECTION = \"geo_test\"\n", | |
"BASE_URL = f\"http://localhost:8983/solr/{DEFAULT_COLLECTION}\"\n", | |
"\n", | |
"def sendJson(doc, collection=DEFAULT_COLLECTION):\n", | |
" '''Sends a single JSON document to solr for indexing\n", | |
"\n", | |
" Args:\n", | |
" doc: dict, to be serialized as JSON\n", | |
" '''\n", | |
" headers = {\"Content-type\": \"application/json\"}\n", | |
" params = {\"commit\":\"true\"}\n", | |
" # This endpoint is a somewhat undocumented mechanism to send \n", | |
" # JSON documents to the Solr update engine\n", | |
" url = f\"{BASE_URL}/update/json/docs\"\n", | |
" data = json.dumps(doc)\n", | |
" res = requests.post(url, params=params, headers=headers, data=data)\n", | |
" logging.debug(res.status_code)\n", | |
" logging.debug(res.text)\n", | |
"\n", | |
"\n", | |
"def solrGet(_id):\n", | |
" \"\"\"\n", | |
" Get document by id using Solr realtime get.\n", | |
"\n", | |
" Args:\n", | |
" _id: str, Document identifier \n", | |
" \"\"\"\n", | |
" headers = {\"Accept\": \"application/json\"}\n", | |
" params = {\"id\":_id, \"wt\":\"json\"}\n", | |
" url = f\"{BASE_URL}/get\"\n", | |
" return requests.get(url, headers=headers, params=params).json()\n", | |
"\n", | |
"\n", | |
"def solrDelete(_id=None):\n", | |
" \"\"\"\n", | |
" Delete documents from Solr\n", | |
"\n", | |
" Args:\n", | |
" _id: str, if set then delete specific document, otherwise delete everything\n", | |
" \"\"\"\n", | |
" headers = {\"Accept\": \"application/json\", \"Content-type\":\"application/json\"}\n", | |
" params = {\"commit\":\"true\"}\n", | |
" body = json.dumps({\"delete\":{\"id\":_id}})\n", | |
" if _id is None:\n", | |
" body = json.dumps({\"delete\":{\"query\":\"*:*\"}})\n", | |
" url = f\"{BASE_URL}/update\"\n", | |
" res = requests.post(url, headers=headers, params=params, data=body)\n", | |
" logging.debug(res.text)\n", | |
"\n", | |
"\n", | |
"def getGeoJSON(q, fl=\"*\", field=\"loc_geo\"):\n", | |
" '''Solr GeoJSON writer only appears to work with RPT using JTS.\n", | |
"\n", | |
" This method generalizes the op to work with other field types\n", | |
" '''\n", | |
" headers = {\"Accept\":\"application/json\"}\n", | |
" params = {\n", | |
" \"q\":q,\n", | |
" \"rows\":9999,\n", | |
" \"fl\": fl,\n", | |
" \"wt\":\"geojson\",\n", | |
" \"geojson.field\":field,\n", | |
" }\n", | |
" url = f\"{BASE_URL}/select\"\n", | |
" res = requests.get(url, headers=headers, params=params).json()\n", | |
" return res[\"response\"]\n", | |
"\n", | |
"def WKTtoGeoJson(ws:str):\n", | |
" g = shapely.wkt.loads(ws)\n", | |
" return shapely.geometry.mapping(g)\n", | |
"\n", | |
"def getGeoJSON2(q, fl=\"*\", field=\"loc_geo\"):\n", | |
" '''Construct GeoJSON from Solr JSON response. \n", | |
" \n", | |
" This works for field types other that RPT with JTS which don't \n", | |
" seem to return correct geojson.\n", | |
" '''\n", | |
" headers = {\"Accept\":\"application/json\"}\n", | |
" params = {\n", | |
" \"q\": q,\n", | |
" \"rows\": 9999,\n", | |
" \"fl\": fl,\n", | |
" \"wt\": \"json\"\n", | |
" }\n", | |
" url = f\"{BASE_URL}/select\"\n", | |
" res = requests.get(url, headers=headers, params=params).json()\n", | |
" response = res.get(\"response\", {})\n", | |
" geo_json = {\n", | |
" \"type\":\"FeatureCollection\",\n", | |
" \"numFound\": response[\"numFound\"],\n", | |
" \"start\": response[\"start\"],\n", | |
" \"numFoundExact\": response[\"numFoundExact\"],\n", | |
" \"features\":[]\n", | |
" }\n", | |
" for doc in response.get(\"docs\", []):\n", | |
" gj = WKTtoGeoJson(doc.get(field, None))\n", | |
" if not gj is None:\n", | |
" feat = {\"type\":\"Feature\", \"geometry\":gj, \"properties\":{}}\n", | |
" for k,v in doc.items():\n", | |
" if k != field:\n", | |
" feat[\"properties\"][k] = v\n", | |
" geo_json[\"features\"].append(feat)\n", | |
" return geo_json\n", | |
"\n", | |
"\n", | |
"def geoJsonToWKT(gj):\n", | |
" if gj.get(\"type\") == \"FeatureCollection\":\n", | |
" res = []\n", | |
" for feature in gj.get(\"features\", {}):\n", | |
" wkt = geoJsonToWKT(feature)\n", | |
" if wkt is not None:\n", | |
" res.append(wkt)\n", | |
" return res\n", | |
" if gj.get(\"type\") == \"Feature\":\n", | |
" gw = shapely.geometry.shape(gj.get(\"geometry\", {}))\n", | |
" return gw.wkt\n", | |
" return None\n", | |
"\n", | |
"\n", | |
"def shapelyToSolr(shape):\n", | |
" centroid = shape.centroid\n", | |
" bb = shape.bounds\n", | |
" res = {\n", | |
" \"loc_ll\": f\"{centroid.y},{centroid.x}\",\n", | |
" \"loc_bb\":f\"ENVELOPE({bb[0]}, {bb[2]}, {bb[3]}, {bb[1]})\",\n", | |
" \"loc_rpt\": shape.wkt\n", | |
" }\n", | |
" return res\n", | |
"\n", | |
"\n", | |
"def geoJsonToSolr(gj):\n", | |
" '''Given a GeoJSON object, compute the Solr index properties:\n", | |
" - centroid\n", | |
" - bounding box\n", | |
" - geometry\n", | |
" '''\n", | |
" gg = gj\n", | |
" #TODO: combine multiple shapes in shapely\n", | |
" if gj.get(\"type\") == \"FeatureCollection\":\n", | |
" feats = gj.get(\"features\",[])\n", | |
" gg = feats[0].get(\"geometry\", {})\n", | |
" shape = shapely.geometry.shape(gg)\n", | |
" return shapelyToSolr(shape)\n", | |
"\n", | |
"\n", | |
"def WKTtoSolr(wkt):\n", | |
" '''Generate the Solr index fields given a WKT string\n", | |
" '''\n", | |
" shape = shapely.wkt.loads(wkt)\n", | |
" return shapelyToSolr(shape)\n", | |
"\n", | |
"\n", | |
"def latitudeLongitudeToSolr(lat, lon):\n", | |
" '''Generate Solr index field values given latitude and longitude \n", | |
" '''\n", | |
" return shapelyToSolr(shapely.geometry.Point(lon, lat))\n", | |
"\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:8983\n", | |
"DEBUG:urllib3.connectionpool:http://localhost:8983 \"POST /solr/geo_test/update?commit=true HTTP/1.1\" 200 68\n", | |
"DEBUG:root:{\n", | |
" \"responseHeader\":{\n", | |
" \"rf\":1,\n", | |
" \"status\":0,\n", | |
" \"QTime\":84}}\n", | |
"\n" | |
] | |
} | |
], | |
"source": [ | |
"# clear the index\n", | |
"solrDelete()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:8983\n", | |
"DEBUG:urllib3.connectionpool:http://localhost:8983 \"POST /solr/geo_test/update/json/docs?commit=true HTTP/1.1\" 200 69\n", | |
"DEBUG:root:200\n", | |
"DEBUG:root:{\n", | |
" \"responseHeader\":{\n", | |
" \"rf\":1,\n", | |
" \"status\":0,\n", | |
" \"QTime\":107}}\n", | |
"\n" | |
] | |
} | |
], | |
"source": [ | |
"# create a record with a single point\n", | |
"record = latitudeLongitudeToSolr(-5,-45)\n", | |
"record[\"id\"] = \"g0\"\n", | |
"sendJson(record)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:8983\n", | |
"DEBUG:urllib3.connectionpool:http://localhost:8983 \"POST /solr/geo_test/update/json/docs?commit=true HTTP/1.1\" 200 69\n", | |
"DEBUG:root:200\n", | |
"DEBUG:root:{\n", | |
" \"responseHeader\":{\n", | |
" \"rf\":1,\n", | |
" \"status\":0,\n", | |
" \"QTime\":128}}\n", | |
"\n" | |
] | |
}, | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"{\"loc_ll\": \"36.27428708808831,33.082677294282036\", \"loc_bb\": \"ENVELOPE(29.487304687499996, 36.826171875, 39.740986355883564, 34.161818161230386)\", \"loc_rpt\": \"LINESTRING (31.81640625 39.74098635588356, 29.4873046875 36.87962060502676, 31.552734375 34.16181816123039, 36.298828125 35.13787911963419, 36.826171875 37.68382032669382, 36.5625 38.92522904714054)\", \"id\": \"g1\"}\n" | |
] | |
} | |
], | |
"source": [ | |
"# Add a linestring to solr\n", | |
"geo = {\n", | |
" \"type\": \"FeatureCollection\",\n", | |
" \"features\": [\n", | |
" {\n", | |
" \"type\": \"Feature\",\n", | |
" \"properties\": {},\n", | |
" \"geometry\": {\n", | |
" \"type\": \"LineString\",\n", | |
" \"coordinates\": [\n", | |
" [\n", | |
" 31.81640625,\n", | |
" 39.740986355883564\n", | |
" ],\n", | |
" [\n", | |
" 29.487304687499996,\n", | |
" 36.87962060502676\n", | |
" ],\n", | |
" [\n", | |
" 31.552734374999996,\n", | |
" 34.161818161230386\n", | |
" ],\n", | |
" [\n", | |
" 36.298828125,\n", | |
" 35.137879119634185\n", | |
" ],\n", | |
" [\n", | |
" 36.826171875,\n", | |
" 37.68382032669382\n", | |
" ],\n", | |
" [\n", | |
" 36.5625,\n", | |
" 38.92522904714054\n", | |
" ]\n", | |
" ]\n", | |
" }\n", | |
" }\n", | |
" ]\n", | |
"}\n", | |
"\n", | |
"record = geoJsonToSolr(geo)\n", | |
"record[\"id\"] = \"g1\"\n", | |
"print(json.dumps(record))\n", | |
"sendJson(record)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:8983\n", | |
"DEBUG:urllib3.connectionpool:http://localhost:8983 \"POST /solr/geo_test/update/json/docs?commit=true HTTP/1.1\" 200 69\n", | |
"DEBUG:root:200\n", | |
"DEBUG:root:{\n", | |
" \"responseHeader\":{\n", | |
" \"rf\":1,\n", | |
" \"status\":0,\n", | |
" \"QTime\":107}}\n", | |
"\n" | |
] | |
} | |
], | |
"source": [ | |
"# Add a bundle of things in WKT string\n", | |
"record = WKTtoSolr(\"GEOMETRYCOLLECTION (POINT (40 10), LINESTRING (10 10, 20 20, 10 40), POLYGON ((40 40, 20 45, 45 30, 40 40)))\")\n", | |
"record[\"id\"] = \"g2\"\n", | |
"sendJson(record)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Dump GEOJson for the records stored so far:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:8983\n", | |
"DEBUG:urllib3.connectionpool:http://localhost:8983 \"GET /solr/geo_test/select?q=%2A%3A%2A&rows=9999&fl=%2A&wt=geojson&geojson.field=loc_rpt HTTP/1.1\" 200 1909\n" | |
] | |
}, | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"{\n", | |
" \"type\": \"FeatureCollection\",\n", | |
" \"numFound\": 3,\n", | |
" \"start\": 0,\n", | |
" \"numFoundExact\": true,\n", | |
" \"features\": [\n", | |
" {\n", | |
" \"type\": \"Feature\",\n", | |
" \"geometry\": {\n", | |
" \"type\": \"Point\",\n", | |
" \"coordinates\": [\n", | |
" -45,\n", | |
" -5\n", | |
" ]\n", | |
" },\n", | |
" \"properties\": {\n", | |
" \"loc_ll\": \"-5.0,-45.0\",\n", | |
" \"loc_bb\": \"ENVELOPE(-45.0, -45.0, -5.0, -5.0)\",\n", | |
" \"id\": \"g0\",\n", | |
" \"loc_bb__minY\": -5.0,\n", | |
" \"loc_bb__minX\": -45.0,\n", | |
" \"loc_bb__maxY\": -5.0,\n", | |
" \"loc_bb__maxX\": -45.0,\n", | |
" \"_version_\": 1707188922527776768\n", | |
" }\n", | |
" },\n", | |
" {\n", | |
" \"type\": \"Feature\",\n", | |
" \"geometry\": {\n", | |
" \"type\": \"LineString\",\n", | |
" \"coordinates\": [\n", | |
" [\n", | |
" 31.816406,\n", | |
" 39.740986\n", | |
" ],\n", | |
" [\n", | |
" 29.487305,\n", | |
" 36.879621\n", | |
" ],\n", | |
" [\n", | |
" 31.552734,\n", | |
" 34.161818\n", | |
" ],\n", | |
" [\n", | |
" 36.298828,\n", | |
" 35.137879\n", | |
" ],\n", | |
" [\n", | |
" 36.826172,\n", | |
" 37.68382\n", | |
" ],\n", | |
" [\n", | |
" 36.5625,\n", | |
" 38.925229\n", | |
" ]\n", | |
" ]\n", | |
" },\n", | |
" \"properties\": {\n", | |
" \"loc_ll\": \"36.27428708808831,33.082677294282036\",\n", | |
" \"loc_bb\": \"ENVELOPE(29.487304687499996, 36.826171875, 39.740986355883564, 34.161818161230386)\",\n", | |
" \"id\": \"g1\",\n", | |
" \"loc_bb__minY\": 34.161818161230386,\n", | |
" \"loc_bb__minX\": 29.487304687499996,\n", | |
" \"loc_bb__maxY\": 39.740986355883564,\n", | |
" \"loc_bb__maxX\": 36.826171875,\n", | |
" \"_version_\": 1707188925299163136\n", | |
" }\n", | |
" },\n", | |
" {\n", | |
" \"type\": \"Feature\",\n", | |
" \"geometry\": {\n", | |
" \"type\": \"GeometryCollection\",\n", | |
" \"geometries\": [\n", | |
" {\n", | |
" \"type\": \"Point\",\n", | |
" \"coordinates\": [\n", | |
" 40,\n", | |
" 10\n", | |
" ]\n", | |
" },\n", | |
" {\n", | |
" \"type\": \"LineString\",\n", | |
" \"coordinates\": [\n", | |
" [\n", | |
" 10,\n", | |
" 10\n", | |
" ],\n", | |
" [\n", | |
" 20,\n", | |
" 20\n", | |
" ],\n", | |
" [\n", | |
" 10,\n", | |
" 40\n", | |
" ]\n", | |
" ]\n", | |
" },\n", | |
" {\n", | |
" \"type\": \"Polygon\",\n", | |
" \"coordinates\": [\n", | |
" [\n", | |
" [\n", | |
" 40,\n", | |
" 40\n", | |
" ],\n", | |
" [\n", | |
" 20,\n", | |
" 45\n", | |
" ],\n", | |
" [\n", | |
" 45,\n", | |
" 30\n", | |
" ],\n", | |
" [\n", | |
" 40,\n", | |
" 40\n", | |
" ]\n", | |
" ]\n", | |
" ]\n", | |
" }\n", | |
" ]\n", | |
" },\n", | |
" \"properties\": {\n", | |
" \"loc_ll\": \"38.33333333333333,35.0\",\n", | |
" \"loc_bb\": \"ENVELOPE(10.0, 45.0, 45.0, 10.0)\",\n", | |
" \"id\": \"g2\",\n", | |
" \"loc_bb__minY\": 10.0,\n", | |
" \"loc_bb__minX\": 10.0,\n", | |
" \"loc_bb__maxY\": 45.0,\n", | |
" \"loc_bb__maxX\": 45.0,\n", | |
" \"_version_\": 1707188929917091840\n", | |
" }\n", | |
" }\n", | |
" ]\n", | |
"}\n" | |
] | |
} | |
], | |
"source": [ | |
"gj = getGeoJSON(\"*:*\", field=\"loc_rpt\")\n", | |
"print(json.dumps(gj, indent=2))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/geo+json": { | |
"features": [ | |
{ | |
"geometry": { | |
"coordinates": [ | |
-45, | |
-5 | |
], | |
"type": "Point" | |
}, | |
"properties": { | |
"_version_": 1707188922527776800, | |
"id": "g0", | |
"loc_bb": "ENVELOPE(-45.0, -45.0, -5.0, -5.0)", | |
"loc_bb__maxX": -45, | |
"loc_bb__maxY": -5, | |
"loc_bb__minX": -45, | |
"loc_bb__minY": -5, | |
"loc_ll": "-5.0,-45.0" | |
}, | |
"type": "Feature" | |
}, | |
{ | |
"geometry": { | |
"coordinates": [ | |
[ | |
31.816406, | |
39.740986 | |
], | |
[ | |
29.487305, | |
36.879621 | |
], | |
[ | |
31.552734, | |
34.161818 | |
], | |
[ | |
36.298828, | |
35.137879 | |
], | |
[ | |
36.826172, | |
37.68382 | |
], | |
[ | |
36.5625, | |
38.925229 | |
] | |
], | |
"type": "LineString" | |
}, | |
"properties": { | |
"_version_": 1707188925299163100, | |
"id": "g1", | |
"loc_bb": "ENVELOPE(29.487304687499996, 36.826171875, 39.740986355883564, 34.161818161230386)", | |
"loc_bb__maxX": 36.826171875, | |
"loc_bb__maxY": 39.740986355883564, | |
"loc_bb__minX": 29.487304687499996, | |
"loc_bb__minY": 34.161818161230386, | |
"loc_ll": "36.27428708808831,33.082677294282036" | |
}, | |
"type": "Feature" | |
}, | |
{ | |
"geometry": { | |
"geometries": [ | |
{ | |
"coordinates": [ | |
40, | |
10 | |
], | |
"type": "Point" | |
}, | |
{ | |
"coordinates": [ | |
[ | |
10, | |
10 | |
], | |
[ | |
20, | |
20 | |
], | |
[ | |
10, | |
40 | |
] | |
], | |
"type": "LineString" | |
}, | |
{ | |
"coordinates": [ | |
[ | |
[ | |
40, | |
40 | |
], | |
[ | |
20, | |
45 | |
], | |
[ | |
45, | |
30 | |
], | |
[ | |
40, | |
40 | |
] | |
] | |
], | |
"type": "Polygon" | |
} | |
], | |
"type": "GeometryCollection" | |
}, | |
"properties": { | |
"_version_": 1707188929917091800, | |
"id": "g2", | |
"loc_bb": "ENVELOPE(10.0, 45.0, 45.0, 10.0)", | |
"loc_bb__maxX": 45, | |
"loc_bb__maxY": 45, | |
"loc_bb__minX": 10, | |
"loc_bb__minY": 10, | |
"loc_ll": "38.33333333333333,35.0" | |
}, | |
"type": "Feature" | |
} | |
], | |
"numFound": 3, | |
"numFoundExact": true, | |
"start": 0, | |
"type": "FeatureCollection" | |
}, | |
"text/plain": [ | |
"<IPython.display.GeoJSON object>" | |
] | |
}, | |
"metadata": { | |
"application/geo+json": { | |
"expanded": false, | |
"root": "root" | |
} | |
}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"# Show the shapes on a map using the simple GeoJSON viewer\n", | |
"GeoJSON(gj)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 16, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/vnd.jupyter.widget-view+json": { | |
"model_id": "f34c8b1f1e86470185d52e46747bf7e4", | |
"version_major": 2, | |
"version_minor": 0 | |
}, | |
"text/plain": [ | |
"Map(center=[20.0, 5.3], controls=(ZoomControl(options=['position', 'zoom_in_text', 'zoom_in_title', 'zoom_out_…" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"# A more sophisticated viewer using Leaflet\n", | |
"\n", | |
"import random\n", | |
"import ipyleaflet\n", | |
"\n", | |
"def random_color(feature):\n", | |
" return {\n", | |
" 'color': 'black',\n", | |
" 'fillColor': random.choice(['red', 'yellow', 'green', 'orange']),\n", | |
" }\n", | |
"\n", | |
"m = ipyleaflet.Map(center=(20.0, 5.3), zoom=3)\n", | |
"geo_json = ipyleaflet.GeoJSON(\n", | |
" data=gj,\n", | |
" style={\n", | |
" 'opacity': 1, 'dashArray': '9', 'fillOpacity': 0.1, 'weight': 1\n", | |
" },\n", | |
" hover_style={\n", | |
" 'color': 'white', 'dashArray': '0', 'fillOpacity': 0.5\n", | |
" },\n", | |
" style_callback=random_color\n", | |
")\n", | |
"m.add_layer(geo_json)\n", | |
"\n", | |
"m" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Heatmaps\n", | |
"\n", | |
"Heatmaps provide a count of features overlapping grid squares. The values are presented in the `counts_ints2D` list of lists. \n", | |
"\n", | |
"The `gridLevel` property determines the number of rows and columns and is determined by interaction between the bounding box and the `distErr` property. Generally good to leave this `None`. For default RPT properties and global views, a max of 3 works otherwise an exception is thrown from too many cells." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 8, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): localhost:8983\n", | |
"DEBUG:urllib3.connectionpool:http://localhost:8983 \"GET /solr/geo_test/select?q=%2A%3A%2A&rows=0&wt=json&facet=true&facet.heatmap=loc_rpt&facet.heatmap.geom=%5B-180.0+-90.0+TO+180.0+90.0%5D&facet.heatmap.gridLevel=2 HTTP/1.1\" 200 5388\n" | |
] | |
}, | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"{\"responseHeader\": {\"zkConnected\": true, \"status\": 0, \"QTime\": 0, \"params\": {\"facet.heatmap.gridLevel\": \"2\", \"q\": \"*:*\", \"facet.heatmap\": \"loc_rpt\", \"facet.heatmap.geom\": \"[-180.0 -90.0 TO 180.0 90.0]\", \"rows\": \"0\", \"wt\": \"json\", \"facet\": \"true\"}}, \"response\": {\"numFound\": 3, \"start\": 0, \"numFoundExact\": true, \"docs\": []}, \"facet_counts\": {\"facet_queries\": {}, \"facet_fields\": {}, \"facet_ranges\": {}, \"facet_intervals\": {}, \"facet_heatmaps\": {\"loc_rpt\": {\"gridLevel\": 2, \"columns\": 32, \"rows\": 32, \"minX\": -180.0, \"maxX\": 180.0, \"minY\": -90.0, \"maxY\": 90.0, \"counts_ints2D\": [null, null, null, null, null, null, null, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], null, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]}}}}\n" | |
] | |
} | |
], | |
"source": [ | |
"default_bb = {\n", | |
" \"minx\":-180.0,\n", | |
" \"miny\":-90.0,\n", | |
" \"maxx\": 180.0,\n", | |
" \"maxy\": 90.0\n", | |
"}\n", | |
"\n", | |
"def solrHeatmap(q, field, bb=default_bb, grid_level=None):\n", | |
" headers = {\"Accept\": \"application/json\"}\n", | |
" params = {\n", | |
" \"q\": q,\n", | |
" \"rows\":0,\n", | |
" \"wt\":\"json\",\n", | |
" \"facet\":\"true\",\n", | |
" \"facet.heatmap\": field,\n", | |
" \"facet.heatmap.geom\": f\"[{bb['minx']} {bb['miny']} TO {bb['maxx']} {bb['maxy']}]\"\n", | |
" }\n", | |
" if not grid_level is None:\n", | |
" params[\"facet.heatmap.gridLevel\"] = grid_level\n", | |
" url = f\"{BASE_URL}/select\"\n", | |
" res = requests.get(url, headers=headers, params=params).json()\n", | |
" return res\n", | |
"\n", | |
"print(json.dumps(solrHeatmap(\"*:*\", \"loc_rpt\", grid_level=2)))" | |
] | |
} | |
], | |
"metadata": { | |
"interpreter": { | |
"hash": "5f395b2d31c77b3803f4db4fdfaf006703467cc699cde2dbad7428ad23403050" | |
}, | |
"kernelspec": { | |
"display_name": "Python 3 (ipykernel)", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.9.1" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 4 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment