[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-\u002Fblog\u002Fenrich-snowflake-table-dls-gps-coordinates":3},{"id":4,"title":5,"body":6,"cover":1478,"date":1479,"description":1480,"extension":1481,"meta":1482,"navigation":469,"path":1485,"seo":1486,"stem":1487,"tags":1488,"__hash__":1494},"blog\u002Fblog\u002Fenrich-snowflake-table-dls-gps-coordinates.md","How to Enrich a Snowflake Table with DLS GPS Coordinates",{"type":7,"value":8,"toc":1468},"minimark",[9,18,25,36,39,44,47,70,78,96,100,107,154,164,168,171,180,183,189,193,196,255,258,265,273,277,280,371,374,378,381,1361,1373,1376,1380,1383,1414,1422,1426,1457,1464],[10,11,12,13,17],"p",{},"Your AER wells table in Snowflake has 10,000 rows. Every row has a DLS legal land description like ",[14,15,16],"code",{},"NW-25-24-1-W5"," — but no latitude, no longitude. Your BI team wants those wells on a map. Your data scientists need spatial joins. Someone suggests exporting to CSV, running the conversions externally, and loading the results back. That process worked once, six months ago. It hasn't been re-run since.",[10,19,20,21,24],{},"There's a better approach: convert Snowflake DLS legal land descriptions to GPS coordinates directly in SQL, without leaving the warehouse. Township Canada's External Function lets you write ",[14,22,23],{},"SELECT TOWNSHIP_CONVERT(legal_desc) FROM wells"," and get coordinates back inline.",[10,26,27,28,31,32,35],{},"If you are running the Snowflake Native App, use the app-provisioned function name ",[14,29,30],{},"TOWNSHIP_CANADA_CONVERT(...)"," instead of ",[14,33,34],{},"TOWNSHIP_CONVERT(...)",".",[10,37,38],{},"Here's how to set it up, run it, and scale it.",[40,41,43],"h2",{"id":42},"prerequisites","Prerequisites",[10,45,46],{},"Before you start, you need two things:",[48,49,50,64],"ol",{},[51,52,53,57,58,63],"li",{},[54,55,56],"strong",{},"A Township Canada Batch API key"," — subscribe on the ",[59,60,62],"a",{"href":61},"\u002Fapp\u002Fapi","API page",". Batch starts at $40\u002Fmo for 1,000 requests and is required for Snowflake table enrichment workflows.",[51,65,66,69],{},[54,67,68],{},"Snowflake ACCOUNTADMIN or SYSADMIN access"," — creating External Functions requires privileges to set up API integrations and function objects.",[10,71,72,73,77],{},"The ",[59,74,76],{"href":75},"\u002Fguides\u002Fsnowflake-external-function","Snowflake External Function setup guide"," covers the full infrastructure: AWS Lambda deployment, API Gateway configuration, IAM roles, and Snowflake integration objects. Most data engineers complete the setup in under an hour.",[10,79,80,81,84,85,84,88,91,92,95],{},"Teams using the Snowflake Native App also get built-in no-key functions for validation and parsing (",[14,82,83],{},"CORE.VALIDATE_LLD",", ",[14,86,87],{},"CORE.PARSE_LLD",[14,89,90],{},"CORE.STANDARDIZE_LLD",") plus ",[14,93,94],{},"DEMO.LOOKUP"," for sample coordinate lookups.",[40,97,99],{"id":98},"step-1-create-the-external-function","Step 1: Create the External Function",[10,101,102,103,106],{},"Once the Lambda proxy and API Gateway are deployed (per the ",[59,104,105],{"href":75},"setup guide","), register the External Function in Snowflake:",[108,109,114],"pre",{"className":110,"code":111,"language":112,"meta":113,"style":113},"language-sql shiki shiki-themes material-theme-lighter vitesse-light vitesse-dark","CREATE OR REPLACE EXTERNAL FUNCTION TOWNSHIP_CONVERT(lld VARCHAR)\n    RETURNS VARIANT\n    API_INTEGRATION = township_canada_integration\n    HEADERS = ('Content-Type' = 'application\u002Fjson')\n    MAX_BATCH_ROWS = 100\n    AS 'https:\u002F\u002FYOUR_API_ID.execute-api.us-west-2.amazonaws.com\u002Fprod\u002F';\n","sql","",[14,115,116,124,130,136,142,148],{"__ignoreMap":113},[117,118,121],"span",{"class":119,"line":120},"line",1,[117,122,123],{},"CREATE OR REPLACE EXTERNAL FUNCTION TOWNSHIP_CONVERT(lld VARCHAR)\n",[117,125,127],{"class":119,"line":126},2,[117,128,129],{},"    RETURNS VARIANT\n",[117,131,133],{"class":119,"line":132},3,[117,134,135],{},"    API_INTEGRATION = township_canada_integration\n",[117,137,139],{"class":119,"line":138},4,[117,140,141],{},"    HEADERS = ('Content-Type' = 'application\u002Fjson')\n",[117,143,145],{"class":119,"line":144},5,[117,146,147],{},"    MAX_BATCH_ROWS = 100\n",[117,149,151],{"class":119,"line":150},6,[117,152,153],{},"    AS 'https:\u002F\u002FYOUR_API_ID.execute-api.us-west-2.amazonaws.com\u002Fprod\u002F';\n",[10,155,156,157,160,161,35],{},"This function accepts any Canadian legal land description string — DLS quarter sections, LSDs, NTS references — and returns a JSON object with ",[14,158,159],{},"latitude"," and ",[14,162,163],{},"longitude",[40,165,167],{"id":166},"step-2-test-with-a-single-row","Step 2: Test with a Single Row",[10,169,170],{},"Before running against your full table, verify the function works:",[108,172,174],{"className":110,"code":173,"language":112,"meta":113,"style":113},"SELECT TOWNSHIP_CONVERT('NW-25-24-1-W5') AS result;\n",[14,175,176],{"__ignoreMap":113},[117,177,178],{"class":119,"line":120},[117,179,173],{},[10,181,182],{},"The result should return a JSON object with GPS coordinates for the northwest quarter of Section 25, Township 24, Range 1, West of the 5th Meridian.",[10,184,185,186,188],{},"If you see an error, check your API key configuration and Lambda proxy logs. The ",[59,187,105],{"href":75}," includes a troubleshooting section for common issues.",[40,190,192],{"id":191},"step-3-enrich-a-full-table","Step 3: Enrich a Full Table",[10,194,195],{},"With the function confirmed, run it against your wells data. Here's a practical example using an AER well inventory table:",[108,197,199],{"className":110,"code":198,"language":112,"meta":113,"style":113},"CREATE TABLE wells_enriched AS\nSELECT\n    w.well_id,\n    w.well_name,\n    w.uwi,\n    w.legal_land_description,\n    tc.result:latitude::FLOAT AS latitude,\n    tc.result:longitude::FLOAT AS longitude\nFROM aer_wells w,\nLATERAL (SELECT TOWNSHIP_CONVERT(w.legal_land_description) AS result) tc;\n",[14,200,201,206,211,216,221,226,231,237,243,249],{"__ignoreMap":113},[117,202,203],{"class":119,"line":120},[117,204,205],{},"CREATE TABLE wells_enriched AS\n",[117,207,208],{"class":119,"line":126},[117,209,210],{},"SELECT\n",[117,212,213],{"class":119,"line":132},[117,214,215],{},"    w.well_id,\n",[117,217,218],{"class":119,"line":138},[117,219,220],{},"    w.well_name,\n",[117,222,223],{"class":119,"line":144},[117,224,225],{},"    w.uwi,\n",[117,227,228],{"class":119,"line":150},[117,229,230],{},"    w.legal_land_description,\n",[117,232,234],{"class":119,"line":233},7,[117,235,236],{},"    tc.result:latitude::FLOAT AS latitude,\n",[117,238,240],{"class":119,"line":239},8,[117,241,242],{},"    tc.result:longitude::FLOAT AS longitude\n",[117,244,246],{"class":119,"line":245},9,[117,247,248],{},"FROM aer_wells w,\n",[117,250,252],{"class":119,"line":251},10,[117,253,254],{},"LATERAL (SELECT TOWNSHIP_CONVERT(w.legal_land_description) AS result) tc;\n",[10,256,257],{},"Snowflake batches the API calls automatically — sending rows in groups of up to 100. A 10,000-row table typically processes in two to three minutes, depending on warehouse size and network latency.",[10,259,260],{},[261,262],"img",{"alt":263,"src":264},"Township Canada batch conversion interface","\u002Fimages\u002Fguides\u002Fbatch-conversion.webp",[10,266,267,268,272],{},"For teams already using Township Canada's web-based ",[59,269,271],{"href":270},"\u002Fguides\u002Fbatch-conversion","batch conversion"," for CSV files, this SQL approach moves the same conversion into your warehouse where it can be scheduled and version-controlled.",[40,274,276],{"id":275},"step-4-schedule-recurring-enrichment","Step 4: Schedule Recurring Enrichment",[10,278,279],{},"The real value of in-warehouse conversion is automation. If your wells table updates daily from AER filings, schedule the enrichment to run on the same cadence:",[108,281,283],{"className":110,"code":282,"language":112,"meta":113,"style":113},"CREATE OR REPLACE TASK enrich_wells_daily\n    WAREHOUSE = analytics_wh\n    SCHEDULE = 'USING CRON 0 6 * * * America\u002FEdmonton'\nAS\n    INSERT INTO wells_enriched\n    SELECT\n        w.well_id,\n        w.well_name,\n        w.uwi,\n        w.legal_land_description,\n        tc.result:latitude::FLOAT,\n        tc.result:longitude::FLOAT\n    FROM aer_wells w\n    LEFT JOIN wells_enriched e ON w.well_id = e.well_id\n    CROSS JOIN LATERAL (SELECT TOWNSHIP_CONVERT(w.legal_land_description) AS result) tc\n    WHERE e.well_id IS NULL;\n",[14,284,285,290,295,300,305,310,315,320,325,330,335,341,347,353,359,365],{"__ignoreMap":113},[117,286,287],{"class":119,"line":120},[117,288,289],{},"CREATE OR REPLACE TASK enrich_wells_daily\n",[117,291,292],{"class":119,"line":126},[117,293,294],{},"    WAREHOUSE = analytics_wh\n",[117,296,297],{"class":119,"line":132},[117,298,299],{},"    SCHEDULE = 'USING CRON 0 6 * * * America\u002FEdmonton'\n",[117,301,302],{"class":119,"line":138},[117,303,304],{},"AS\n",[117,306,307],{"class":119,"line":144},[117,308,309],{},"    INSERT INTO wells_enriched\n",[117,311,312],{"class":119,"line":150},[117,313,314],{},"    SELECT\n",[117,316,317],{"class":119,"line":233},[117,318,319],{},"        w.well_id,\n",[117,321,322],{"class":119,"line":239},[117,323,324],{},"        w.well_name,\n",[117,326,327],{"class":119,"line":245},[117,328,329],{},"        w.uwi,\n",[117,331,332],{"class":119,"line":251},[117,333,334],{},"        w.legal_land_description,\n",[117,336,338],{"class":119,"line":337},11,[117,339,340],{},"        tc.result:latitude::FLOAT,\n",[117,342,344],{"class":119,"line":343},12,[117,345,346],{},"        tc.result:longitude::FLOAT\n",[117,348,350],{"class":119,"line":349},13,[117,351,352],{},"    FROM aer_wells w\n",[117,354,356],{"class":119,"line":355},14,[117,357,358],{},"    LEFT JOIN wells_enriched e ON w.well_id = e.well_id\n",[117,360,362],{"class":119,"line":361},15,[117,363,364],{},"    CROSS JOIN LATERAL (SELECT TOWNSHIP_CONVERT(w.legal_land_description) AS result) tc\n",[117,366,368],{"class":119,"line":367},16,[117,369,370],{},"    WHERE e.well_id IS NULL;\n",[10,372,373],{},"This task runs at 6 AM Mountain Time daily, converting only new rows that haven't been enriched yet. No manual re-runs, no stale data, no broken lineage.",[40,375,377],{"id":376},"databricks-udf-variant","Databricks UDF Variant",[10,379,380],{},"If your team runs on Databricks instead of Snowflake, the same Township Canada API integrates through a Python UDF:",[108,382,386],{"className":383,"code":384,"language":385,"meta":113,"style":113},"language-python shiki shiki-themes material-theme-lighter vitesse-light vitesse-dark","from pyspark.sql.functions import pandas_udf\nfrom pyspark.sql.types import StructType, StructField, DoubleType\nimport pandas as pd\nimport requests\n\nTOWNSHIP_API_KEY = dbutils.secrets.get(scope=\"township\", key=\"api_key\")\nTOWNSHIP_API_URL = \"https:\u002F\u002Fdeveloper.townshipcanada.com\u002Fbatch\u002Flegal-location\"\nBATCH_SIZE = 100\n\ndef convert_lld_batch(llds):\n    response = requests.post(\n        TOWNSHIP_API_URL,\n        headers={\n            \"X-API-Key\": TOWNSHIP_API_KEY,\n            \"Content-Type\": \"application\u002Fjson\"\n        },\n        json=llds\n    )\n    response.raise_for_status()\n    result = response.json()\n\n    coords_map = {}\n    for feature in result.get(\"features\", []):\n        props = feature.get(\"properties\", {})\n        geom = feature.get(\"geometry\", {})\n        if props.get(\"shape\") == \"centroid\" and geom.get(\"type\") == \"Point\":\n            lon, lat = geom[\"coordinates\"]\n            coords_map[props[\"legal_location\"]] = (lat, lon)\n    return coords_map\n\n@pandas_udf(StructType([\n    StructField(\"latitude\", DoubleType()),\n    StructField(\"longitude\", DoubleType())\n]))\ndef township_convert(descriptions: pd.Series) -> pd.DataFrame:\n    llds = descriptions.tolist()\n    all_coords = {}\n\n    for i in range(0, len(llds), BATCH_SIZE):\n        chunk = llds[i:i + BATCH_SIZE]\n        all_coords.update(convert_lld_batch(chunk))\n\n    latitudes = []\n    longitudes = []\n    for lld in llds:\n        coord = all_coords.get(lld)\n        latitudes.append(coord[0] if coord else None)\n        longitudes.append(coord[1] if coord else None)\n\n    return pd.DataFrame({\"latitude\": latitudes, \"longitude\": longitudes})\n","python",[14,387,388,414,445,458,465,471,532,548,559,563,582,600,609,617,635,653,658,669,675,689,707,712,723,756,785,812,881,909,947,956,961,978,1000,1020,1026,1063,1081,1091,1096,1132,1160,1184,1189,1200,1210,1224,1246,1284,1315,1320],{"__ignoreMap":113},[117,389,390,394,398,401,403,405,408,411],{"class":119,"line":120},[117,391,393],{"class":392},"siDh9","from",[117,395,397],{"class":396},"sftqT"," pyspark",[117,399,35],{"class":400},"soVBu",[117,402,112],{"class":396},[117,404,35],{"class":400},[117,406,407],{"class":396},"functions ",[117,409,410],{"class":392},"import",[117,412,413],{"class":396}," pandas_udf\n",[117,415,416,418,420,422,424,426,429,431,434,437,440,442],{"class":119,"line":126},[117,417,393],{"class":392},[117,419,397],{"class":396},[117,421,35],{"class":400},[117,423,112],{"class":396},[117,425,35],{"class":400},[117,427,428],{"class":396},"types ",[117,430,410],{"class":392},[117,432,433],{"class":396}," StructType",[117,435,436],{"class":400},",",[117,438,439],{"class":396}," StructField",[117,441,436],{"class":400},[117,443,444],{"class":396}," DoubleType\n",[117,446,447,449,452,455],{"class":119,"line":132},[117,448,410],{"class":392},[117,450,451],{"class":396}," pandas ",[117,453,454],{"class":392},"as",[117,456,457],{"class":396}," pd\n",[117,459,460,462],{"class":119,"line":138},[117,461,410],{"class":392},[117,463,464],{"class":396}," requests\n",[117,466,467],{"class":119,"line":144},[117,468,470],{"emptyLinePlaceholder":469},true,"\n",[117,472,473,477,480,483,485,489,491,495,498,502,505,509,513,515,517,520,522,524,527,529],{"class":119,"line":150},[117,474,476],{"class":475},"se3Ec","TOWNSHIP_API_KEY",[117,478,479],{"class":400}," =",[117,481,482],{"class":396}," dbutils",[117,484,35],{"class":400},[117,486,488],{"class":487},"sBPpx","secrets",[117,490,35],{"class":400},[117,492,494],{"class":493},"siWMO","get",[117,496,497],{"class":400},"(",[117,499,501],{"class":500},"sqOPj","scope",[117,503,504],{"class":400},"=",[117,506,508],{"class":507},"sbYkP","\"",[117,510,512],{"class":511},"sTbE_","township",[117,514,508],{"class":507},[117,516,436],{"class":400},[117,518,519],{"class":500}," key",[117,521,504],{"class":400},[117,523,508],{"class":507},[117,525,526],{"class":511},"api_key",[117,528,508],{"class":507},[117,530,531],{"class":400},")\n",[117,533,534,537,539,542,545],{"class":119,"line":233},[117,535,536],{"class":475},"TOWNSHIP_API_URL",[117,538,479],{"class":400},[117,540,541],{"class":507}," \"",[117,543,544],{"class":511},"https:\u002F\u002Fdeveloper.townshipcanada.com\u002Fbatch\u002Flegal-location",[117,546,547],{"class":507},"\"\n",[117,549,550,553,555],{"class":119,"line":239},[117,551,552],{"class":475},"BATCH_SIZE",[117,554,479],{"class":400},[117,556,558],{"class":557},"s7CZa"," 100\n",[117,560,561],{"class":119,"line":245},[117,562,470],{"emptyLinePlaceholder":469},[117,564,565,569,573,575,579],{"class":119,"line":251},[117,566,568],{"class":567},"s5Kfy","def",[117,570,572],{"class":571},"sljsM"," convert_lld_batch",[117,574,497],{"class":400},[117,576,578],{"class":577},"sCyAa","llds",[117,580,581],{"class":400},"):\n",[117,583,584,587,589,592,594,597],{"class":119,"line":337},[117,585,586],{"class":396},"    response ",[117,588,504],{"class":400},[117,590,591],{"class":396}," requests",[117,593,35],{"class":400},[117,595,596],{"class":493},"post",[117,598,599],{"class":400},"(\n",[117,601,602,606],{"class":119,"line":343},[117,603,605],{"class":604},"s6DHA","        TOWNSHIP_API_URL",[117,607,608],{"class":400},",\n",[117,610,611,614],{"class":119,"line":349},[117,612,613],{"class":500},"        headers",[117,615,616],{"class":400},"={\n",[117,618,619,622,625,627,630,633],{"class":119,"line":355},[117,620,621],{"class":507},"            \"",[117,623,624],{"class":511},"X-API-Key",[117,626,508],{"class":507},[117,628,629],{"class":400},":",[117,631,632],{"class":604}," TOWNSHIP_API_KEY",[117,634,608],{"class":400},[117,636,637,639,642,644,646,648,651],{"class":119,"line":361},[117,638,621],{"class":507},[117,640,641],{"class":511},"Content-Type",[117,643,508],{"class":507},[117,645,629],{"class":400},[117,647,541],{"class":507},[117,649,650],{"class":511},"application\u002Fjson",[117,652,547],{"class":507},[117,654,655],{"class":119,"line":367},[117,656,657],{"class":400},"        },\n",[117,659,661,664,666],{"class":119,"line":660},17,[117,662,663],{"class":500},"        json",[117,665,504],{"class":400},[117,667,668],{"class":493},"llds\n",[117,670,672],{"class":119,"line":671},18,[117,673,674],{"class":400},"    )\n",[117,676,678,681,683,686],{"class":119,"line":677},19,[117,679,680],{"class":396},"    response",[117,682,35],{"class":400},[117,684,685],{"class":493},"raise_for_status",[117,687,688],{"class":400},"()\n",[117,690,692,695,697,700,702,705],{"class":119,"line":691},20,[117,693,694],{"class":396},"    result ",[117,696,504],{"class":400},[117,698,699],{"class":396}," response",[117,701,35],{"class":400},[117,703,704],{"class":493},"json",[117,706,688],{"class":400},[117,708,710],{"class":119,"line":709},21,[117,711,470],{"emptyLinePlaceholder":469},[117,713,715,718,720],{"class":119,"line":714},22,[117,716,717],{"class":396},"    coords_map ",[117,719,504],{"class":400},[117,721,722],{"class":400}," {}\n",[117,724,726,729,732,735,738,740,742,744,746,749,751,753],{"class":119,"line":725},23,[117,727,728],{"class":392},"    for",[117,730,731],{"class":396}," feature ",[117,733,734],{"class":392},"in",[117,736,737],{"class":396}," result",[117,739,35],{"class":400},[117,741,494],{"class":493},[117,743,497],{"class":400},[117,745,508],{"class":507},[117,747,748],{"class":511},"features",[117,750,508],{"class":507},[117,752,436],{"class":400},[117,754,755],{"class":400}," []):\n",[117,757,759,762,764,767,769,771,773,775,778,780,782],{"class":119,"line":758},24,[117,760,761],{"class":396},"        props ",[117,763,504],{"class":400},[117,765,766],{"class":396}," feature",[117,768,35],{"class":400},[117,770,494],{"class":493},[117,772,497],{"class":400},[117,774,508],{"class":507},[117,776,777],{"class":511},"properties",[117,779,508],{"class":507},[117,781,436],{"class":400},[117,783,784],{"class":400}," {})\n",[117,786,788,791,793,795,797,799,801,803,806,808,810],{"class":119,"line":787},25,[117,789,790],{"class":396},"        geom ",[117,792,504],{"class":400},[117,794,766],{"class":396},[117,796,35],{"class":400},[117,798,494],{"class":493},[117,800,497],{"class":400},[117,802,508],{"class":507},[117,804,805],{"class":511},"geometry",[117,807,508],{"class":507},[117,809,436],{"class":400},[117,811,784],{"class":400},[117,813,815,818,821,823,825,827,829,832,834,837,841,843,846,848,851,854,856,858,860,862,865,867,869,871,873,876,878],{"class":119,"line":814},26,[117,816,817],{"class":392},"        if",[117,819,820],{"class":396}," props",[117,822,35],{"class":400},[117,824,494],{"class":493},[117,826,497],{"class":400},[117,828,508],{"class":507},[117,830,831],{"class":511},"shape",[117,833,508],{"class":507},[117,835,836],{"class":400},")",[117,838,840],{"class":839},"sVsLi"," ==",[117,842,541],{"class":507},[117,844,845],{"class":511},"centroid",[117,847,508],{"class":507},[117,849,850],{"class":839}," and",[117,852,853],{"class":396}," geom",[117,855,35],{"class":400},[117,857,494],{"class":493},[117,859,497],{"class":400},[117,861,508],{"class":507},[117,863,864],{"class":511},"type",[117,866,508],{"class":507},[117,868,836],{"class":400},[117,870,840],{"class":839},[117,872,541],{"class":507},[117,874,875],{"class":511},"Point",[117,877,508],{"class":507},[117,879,880],{"class":400},":\n",[117,882,884,887,889,892,894,896,899,901,904,906],{"class":119,"line":883},27,[117,885,886],{"class":396},"            lon",[117,888,436],{"class":400},[117,890,891],{"class":396}," lat ",[117,893,504],{"class":400},[117,895,853],{"class":396},[117,897,898],{"class":400},"[",[117,900,508],{"class":507},[117,902,903],{"class":511},"coordinates",[117,905,508],{"class":507},[117,907,908],{"class":400},"]\n",[117,910,912,915,917,920,922,924,927,929,932,934,937,940,942,945],{"class":119,"line":911},28,[117,913,914],{"class":396},"            coords_map",[117,916,898],{"class":400},[117,918,919],{"class":396},"props",[117,921,898],{"class":400},[117,923,508],{"class":507},[117,925,926],{"class":511},"legal_location",[117,928,508],{"class":507},[117,930,931],{"class":400},"]]",[117,933,479],{"class":400},[117,935,936],{"class":400}," (",[117,938,939],{"class":396},"lat",[117,941,436],{"class":400},[117,943,944],{"class":396}," lon",[117,946,531],{"class":400},[117,948,950,953],{"class":119,"line":949},29,[117,951,952],{"class":392},"    return",[117,954,955],{"class":396}," coords_map\n",[117,957,959],{"class":119,"line":958},30,[117,960,470],{"emptyLinePlaceholder":469},[117,962,964,967,970,972,975],{"class":119,"line":963},31,[117,965,966],{"class":400},"@",[117,968,969],{"class":571},"pandas_udf",[117,971,497],{"class":400},[117,973,974],{"class":493},"StructType",[117,976,977],{"class":400},"([\n",[117,979,981,984,986,988,990,992,994,997],{"class":119,"line":980},32,[117,982,983],{"class":493},"    StructField",[117,985,497],{"class":400},[117,987,508],{"class":507},[117,989,159],{"class":511},[117,991,508],{"class":507},[117,993,436],{"class":400},[117,995,996],{"class":493}," DoubleType",[117,998,999],{"class":400},"()),\n",[117,1001,1003,1005,1007,1009,1011,1013,1015,1017],{"class":119,"line":1002},33,[117,1004,983],{"class":493},[117,1006,497],{"class":400},[117,1008,508],{"class":507},[117,1010,163],{"class":511},[117,1012,508],{"class":507},[117,1014,436],{"class":400},[117,1016,996],{"class":493},[117,1018,1019],{"class":400},"())\n",[117,1021,1023],{"class":119,"line":1022},34,[117,1024,1025],{"class":400},"]))\n",[117,1027,1029,1031,1034,1036,1039,1041,1044,1046,1049,1051,1054,1056,1058,1061],{"class":119,"line":1028},35,[117,1030,568],{"class":567},[117,1032,1033],{"class":571}," township_convert",[117,1035,497],{"class":400},[117,1037,1038],{"class":577},"descriptions",[117,1040,629],{"class":400},[117,1042,1043],{"class":396}," pd",[117,1045,35],{"class":400},[117,1047,1048],{"class":487},"Series",[117,1050,836],{"class":400},[117,1052,1053],{"class":400}," ->",[117,1055,1043],{"class":396},[117,1057,35],{"class":400},[117,1059,1060],{"class":487},"DataFrame",[117,1062,880],{"class":400},[117,1064,1066,1069,1071,1074,1076,1079],{"class":119,"line":1065},36,[117,1067,1068],{"class":396},"    llds ",[117,1070,504],{"class":400},[117,1072,1073],{"class":396}," descriptions",[117,1075,35],{"class":400},[117,1077,1078],{"class":493},"tolist",[117,1080,688],{"class":400},[117,1082,1084,1087,1089],{"class":119,"line":1083},37,[117,1085,1086],{"class":396},"    all_coords ",[117,1088,504],{"class":400},[117,1090,722],{"class":400},[117,1092,1094],{"class":119,"line":1093},38,[117,1095,470],{"emptyLinePlaceholder":469},[117,1097,1099,1101,1104,1106,1110,1112,1115,1117,1120,1122,1124,1127,1130],{"class":119,"line":1098},39,[117,1100,728],{"class":392},[117,1102,1103],{"class":396}," i ",[117,1105,734],{"class":392},[117,1107,1109],{"class":1108},"sJdAF"," range",[117,1111,497],{"class":400},[117,1113,1114],{"class":557},"0",[117,1116,436],{"class":400},[117,1118,1119],{"class":1108}," len",[117,1121,497],{"class":400},[117,1123,578],{"class":493},[117,1125,1126],{"class":400},"),",[117,1128,1129],{"class":604}," BATCH_SIZE",[117,1131,581],{"class":400},[117,1133,1135,1138,1140,1143,1145,1148,1150,1153,1156,1158],{"class":119,"line":1134},40,[117,1136,1137],{"class":396},"        chunk ",[117,1139,504],{"class":400},[117,1141,1142],{"class":396}," llds",[117,1144,898],{"class":400},[117,1146,1147],{"class":396},"i",[117,1149,629],{"class":400},[117,1151,1152],{"class":396},"i ",[117,1154,1155],{"class":839},"+",[117,1157,1129],{"class":475},[117,1159,908],{"class":400},[117,1161,1163,1166,1168,1171,1173,1176,1178,1181],{"class":119,"line":1162},41,[117,1164,1165],{"class":396},"        all_coords",[117,1167,35],{"class":400},[117,1169,1170],{"class":493},"update",[117,1172,497],{"class":400},[117,1174,1175],{"class":493},"convert_lld_batch",[117,1177,497],{"class":400},[117,1179,1180],{"class":493},"chunk",[117,1182,1183],{"class":400},"))\n",[117,1185,1187],{"class":119,"line":1186},42,[117,1188,470],{"emptyLinePlaceholder":469},[117,1190,1192,1195,1197],{"class":119,"line":1191},43,[117,1193,1194],{"class":396},"    latitudes ",[117,1196,504],{"class":400},[117,1198,1199],{"class":400}," []\n",[117,1201,1203,1206,1208],{"class":119,"line":1202},44,[117,1204,1205],{"class":396},"    longitudes ",[117,1207,504],{"class":400},[117,1209,1199],{"class":400},[117,1211,1213,1215,1218,1220,1222],{"class":119,"line":1212},45,[117,1214,728],{"class":392},[117,1216,1217],{"class":396}," lld ",[117,1219,734],{"class":392},[117,1221,1142],{"class":396},[117,1223,880],{"class":400},[117,1225,1227,1230,1232,1235,1237,1239,1241,1244],{"class":119,"line":1226},46,[117,1228,1229],{"class":396},"        coord ",[117,1231,504],{"class":400},[117,1233,1234],{"class":396}," all_coords",[117,1236,35],{"class":400},[117,1238,494],{"class":493},[117,1240,497],{"class":400},[117,1242,1243],{"class":493},"lld",[117,1245,531],{"class":400},[117,1247,1249,1252,1254,1257,1259,1262,1264,1266,1269,1272,1275,1278,1282],{"class":119,"line":1248},47,[117,1250,1251],{"class":396},"        latitudes",[117,1253,35],{"class":400},[117,1255,1256],{"class":493},"append",[117,1258,497],{"class":400},[117,1260,1261],{"class":493},"coord",[117,1263,898],{"class":400},[117,1265,1114],{"class":557},[117,1267,1268],{"class":400},"]",[117,1270,1271],{"class":392}," if",[117,1273,1274],{"class":493}," coord ",[117,1276,1277],{"class":392},"else",[117,1279,1281],{"class":1280},"s8XtY"," None",[117,1283,531],{"class":400},[117,1285,1287,1290,1292,1294,1296,1298,1300,1303,1305,1307,1309,1311,1313],{"class":119,"line":1286},48,[117,1288,1289],{"class":396},"        longitudes",[117,1291,35],{"class":400},[117,1293,1256],{"class":493},[117,1295,497],{"class":400},[117,1297,1261],{"class":493},[117,1299,898],{"class":400},[117,1301,1302],{"class":557},"1",[117,1304,1268],{"class":400},[117,1306,1271],{"class":392},[117,1308,1274],{"class":493},[117,1310,1277],{"class":392},[117,1312,1281],{"class":1280},[117,1314,531],{"class":400},[117,1316,1318],{"class":119,"line":1317},49,[117,1319,470],{"emptyLinePlaceholder":469},[117,1321,1323,1325,1327,1329,1331,1334,1336,1338,1340,1342,1345,1347,1349,1351,1353,1355,1358],{"class":119,"line":1322},50,[117,1324,952],{"class":392},[117,1326,1043],{"class":396},[117,1328,35],{"class":400},[117,1330,1060],{"class":493},[117,1332,1333],{"class":400},"({",[117,1335,508],{"class":507},[117,1337,159],{"class":511},[117,1339,508],{"class":507},[117,1341,629],{"class":400},[117,1343,1344],{"class":493}," latitudes",[117,1346,436],{"class":400},[117,1348,541],{"class":507},[117,1350,163],{"class":511},[117,1352,508],{"class":507},[117,1354,629],{"class":400},[117,1356,1357],{"class":493}," longitudes",[117,1359,1360],{"class":400},"})\n",[10,1362,1363,1364,1367,1368,1372],{},"Apply it to a Delta table the same way: ",[14,1365,1366],{},"df.withColumn(\"coords\", township_convert(col(\"legal_land_description\")))",". The ",[59,1369,1371],{"href":1370},"\u002Fguides\u002Fbatch-api-guide","Batch API guide"," covers request format, error handling, and rate limits in detail.",[10,1374,1375],{},"Township Canada also publishes DLS grid boundary polygons as a Delta Sharing dataset — useful for spatial joins against section and township boundaries without individual API calls.",[40,1377,1379],{"id":1378},"performance-and-cost-considerations","Performance and Cost Considerations",[10,1381,1382],{},"A few things to keep in mind at scale:",[1384,1385,1386,1396,1402,1408],"ul",{},[51,1387,1388,1391,1392,1395],{},[54,1389,1390],{},"Batch size",": Snowflake sends rows in batches of up to 100 when ",[14,1393,1394],{},"MAX_BATCH_ROWS = 100",", which matches the Batch API record limit.",[51,1397,1398,1401],{},[54,1399,1400],{},"Rate limits",": Batch API limits are published as requests per second (Build: 1 req\u002Fsec, Scale: 5 req\u002Fsec, Enterprise: 25 req\u002Fsec). Keep that in mind when tuning warehouse concurrency.",[51,1403,1404,1407],{},[54,1405,1406],{},"One-time enrichment",": For stable source data, run enrichment with CTAS and store coordinates so you do not re-call the API on every dashboard query.",[51,1409,1410,1413],{},[54,1411,1412],{},"Cost planning",": Treat pricing as included monthly request volume by tier, not metered per-row billing.",[10,1415,1416,1417,1421],{},"For Canadian ",[59,1418,1420],{"href":1419},"\u002Fblog\u002Foil-gas-legal-land-descriptions","oil and gas teams"," that already track well locations as DLS legal land descriptions, this turns a manual GIS step into a SQL query.",[40,1423,1425],{"id":1424},"get-started","Get Started",[48,1427,1428,1439,1447],{},[51,1429,1430,1433,1434,1438],{},[54,1431,1432],{},"Sign up for a Batch API key"," at ",[59,1435,1437],{"href":1436},"\u002Fapi","townshipcanada.com\u002Fapi"," — starts at $40\u002Fmo",[51,1440,1441,1433,1444,1446],{},[54,1442,1443],{},"Follow the setup guide",[59,1445,75],{"href":75}," to deploy the Lambda proxy and register the External Function",[51,1448,1449,1452,1453,1456],{},[54,1450,1451],{},"Test it",": ",[14,1454,1455],{},"SELECT TOWNSHIP_CONVERT('SE-36-42-3-W5')"," — you should see latitude\u002Flongitude in the returned JSON",[10,1458,1459,1460,35],{},"The guide includes Snowflake DDL, Lambda proxy code, Databricks UDF examples, and troubleshooting steps. If you hit a wall during setup, email ",[59,1461,1463],{"href":1462},"mailto:support@townshipcanada.com","support@townshipcanada.com",[1465,1466,1467],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .siDh9, html code.shiki .siDh9{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#1E754F;--shiki-default-font-style:inherit;--shiki-dark:#4D9375;--shiki-dark-font-style:inherit}html pre.shiki code .sftqT, html code.shiki .sftqT{--shiki-light:#90A4AE;--shiki-default:#393A34;--shiki-dark:#DBD7CAEE}html pre.shiki code .soVBu, html code.shiki .soVBu{--shiki-light:#39ADB5;--shiki-default:#999999;--shiki-dark:#666666}html pre.shiki code .se3Ec, html code.shiki .se3Ec{--shiki-light:#90A4AE;--shiki-default:#A65E2B;--shiki-dark:#C99076}html pre.shiki code .sBPpx, html code.shiki .sBPpx{--shiki-light:#E53935;--shiki-default:#393A34;--shiki-dark:#DBD7CAEE}html pre.shiki code .siWMO, html code.shiki .siWMO{--shiki-light:#6182B8;--shiki-default:#393A34;--shiki-dark:#DBD7CAEE}html pre.shiki code .sqOPj, html code.shiki .sqOPj{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#B07D48;--shiki-default-font-style:inherit;--shiki-dark:#BD976A;--shiki-dark-font-style:inherit}html pre.shiki code .sbYkP, html code.shiki .sbYkP{--shiki-light:#39ADB5;--shiki-default:#B5695977;--shiki-dark:#C98A7D77}html pre.shiki code .sTbE_, html code.shiki .sTbE_{--shiki-light:#91B859;--shiki-default:#B56959;--shiki-dark:#C98A7D}html pre.shiki code .s7CZa, html code.shiki .s7CZa{--shiki-light:#F76D47;--shiki-default:#2F798A;--shiki-dark:#4C9A91}html pre.shiki code .s5Kfy, html code.shiki .s5Kfy{--shiki-light:#9C3EDA;--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .sljsM, html code.shiki .sljsM{--shiki-light:#6182B8;--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .sCyAa, html code.shiki .sCyAa{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#393A34;--shiki-default-font-style:inherit;--shiki-dark:#DBD7CAEE;--shiki-dark-font-style:inherit}html pre.shiki code .s6DHA, html code.shiki .s6DHA{--shiki-light:#6182B8;--shiki-default:#A65E2B;--shiki-dark:#C99076}html pre.shiki code .sVsLi, html code.shiki .sVsLi{--shiki-light:#39ADB5;--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .sJdAF, html code.shiki .sJdAF{--shiki-light:#6182B8;--shiki-default:#998418;--shiki-dark:#B8A965}html pre.shiki code .s8XtY, html code.shiki .s8XtY{--shiki-light:#39ADB5;--shiki-default:#1E754F;--shiki-dark:#4D9375}",{"title":113,"searchDepth":126,"depth":126,"links":1469},[1470,1471,1472,1473,1474,1475,1476,1477],{"id":42,"depth":126,"text":43},{"id":98,"depth":126,"text":99},{"id":166,"depth":126,"text":167},{"id":191,"depth":126,"text":192},{"id":275,"depth":126,"text":276},{"id":376,"depth":126,"text":377},{"id":1378,"depth":126,"text":1379},{"id":1424,"depth":126,"text":1425},"https:\u002F\u002Fb9bukyyl5yuyveqq.public.blob.vercel-storage.com\u002Fimages\u002Fblog\u002F2026-04\u002F5f236977-5b54-44eb-92f5-37e552cff792.jpeg","2026-04-01","Step-by-step guide to converting Snowflake DLS legal land description columns to GPS coordinates using Township Canada's External Function.","md",{"category":1483,"author":1484},"guides","Township Canada","\u002Fblog\u002Fenrich-snowflake-table-dls-gps-coordinates",{"title":5,"description":1480},"blog\u002Fenrich-snowflake-table-dls-gps-coordinates",[1489,1490,1491,1492,1493],"Snowflake","API","Oil and Gas","Developer","Integration","LOKfJHfUsEF42fGLKqavhaym_dn9dKEzQUHX1evaDzw"]