[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-\u002Fblog\u002Fconvert-legal-land-descriptions-snowflake-sql":3},{"id":4,"title":5,"author":6,"body":7,"category":587,"cover":588,"date":589,"description":590,"extension":591,"meta":592,"navigation":103,"path":593,"seo":594,"stem":595,"tags":596,"__hash__":602},"blog\u002Fblog\u002Fconvert-legal-land-descriptions-snowflake-sql.md","How to Convert Legal Land Descriptions Directly in Snowflake SQL","Township Canada",{"type":8,"value":9,"toc":577},"minimark",[10,27,30,35,38,49,65,68,72,75,117,128,131,173,181,186,189,230,233,240,244,247,276,284,288,291,306,309,346,364,367,404,407,411,414,483,486,520,523,527,533,541,562,570,573],[11,12,13,14,18,19,22,23,26],"p",{},"You've loaded a Canadian wells or parcels dataset into Snowflake. The ",[15,16,17],"code",{},"legal_description"," column holds values like ",[15,20,21],{},"NW-25-24-1-W5",", ",[15,24,25],{},"LSD 06-32-048-07W5",", maybe NTS block references mixed in. Your dashboard needs GPS coordinates. Your data team is about to export to CSV.",[11,28,29],{},"Stop there. You can convert legal land descriptions directly in Snowflake SQL - no export, no Python script, no broken data lineage. Here's how, covering the SQL patterns you'll actually use and the one validation step most teams skip.",[31,32,34],"h2",{"id":33},"two-paths-for-sql-native-conversion","Two Paths for SQL-Native Conversion",[11,36,37],{},"Township Canada supports two ways to run legal land description conversion inside Snowflake:",[11,39,40,44,45,48],{},[41,42,43],"strong",{},"Snowflake Native App"," - Install from the Snowflake Marketplace. No AWS infrastructure required. Once installed, it registers the conversion function as ",[15,46,47],{},"TOWNSHIP_CANADA_CONVERT"," plus a set of built-in utility functions for format validation and parsing. The fastest path for teams that want to start converting and skip the Lambda setup entirely.",[11,50,51,54,55,58,59,64],{},[41,52,53],{},"External Function via AWS Lambda"," - Deploy an AWS Lambda proxy, configure an API Gateway endpoint, and register the function in Snowflake as ",[15,56,57],{},"TOWNSHIP_CONVERT",". The ",[60,61,63],"a",{"href":62},"\u002Fguides\u002Fsnowflake-external-function","setup guide"," walks through every step - Lambda deployment, API Gateway, IAM roles, and Snowflake integration objects. Most data engineers complete it in under an hour. This option suits teams that manage Snowflake in a private VPC with controlled outbound network access.",[11,66,67],{},"Both call the same Township Canada Batch API and return GPS coordinates in the same JSON structure. The SQL patterns below work with either - just swap the function name.",[31,69,71],{"id":70},"core-conversion-patterns","Core Conversion Patterns",[11,73,74],{},"Before running against a production table, test with a known description:",[76,77,82],"pre",{"className":78,"code":79,"language":80,"meta":81,"style":81},"language-sql shiki shiki-themes material-theme-lighter vitesse-light vitesse-dark","-- Native App\nSELECT TOWNSHIP_CANADA_CONVERT('NW-25-24-1-W5');\n\n-- External Function\nSELECT TOWNSHIP_CONVERT('NW-25-24-1-W5');\n","sql","",[15,83,84,92,98,105,111],{"__ignoreMap":81},[85,86,89],"span",{"class":87,"line":88},"line",1,[85,90,91],{},"-- Native App\n",[85,93,95],{"class":87,"line":94},2,[85,96,97],{},"SELECT TOWNSHIP_CANADA_CONVERT('NW-25-24-1-W5');\n",[85,99,101],{"class":87,"line":100},3,[85,102,104],{"emptyLinePlaceholder":103},true,"\n",[85,106,108],{"class":87,"line":107},4,[85,109,110],{},"-- External Function\n",[85,112,114],{"class":87,"line":113},5,[85,115,116],{},"SELECT TOWNSHIP_CONVERT('NW-25-24-1-W5');\n",[11,118,119,120,123,124,127],{},"Both return a JSON variant with ",[15,121,122],{},"latitude"," and ",[15,125,126],{},"longitude"," fields for the northwest quarter of Section 25, Township 24, Range 1, West of the 5th Meridian.",[11,129,130],{},"To extract coordinates as separate float columns:",[76,132,134],{"className":78,"code":133,"language":80,"meta":81,"style":81},"SELECT\n    well_id,\n    legal_description,\n    TOWNSHIP_CONVERT(legal_description):latitude::FLOAT  AS latitude,\n    TOWNSHIP_CONVERT(legal_description):longitude::FLOAT AS longitude\nFROM well_inventory\nWHERE region = 'Cardium_Play';\n",[15,135,136,141,146,151,156,161,167],{"__ignoreMap":81},[85,137,138],{"class":87,"line":88},[85,139,140],{},"SELECT\n",[85,142,143],{"class":87,"line":94},[85,144,145],{},"    well_id,\n",[85,147,148],{"class":87,"line":100},[85,149,150],{},"    legal_description,\n",[85,152,153],{"class":87,"line":107},[85,154,155],{},"    TOWNSHIP_CONVERT(legal_description):latitude::FLOAT  AS latitude,\n",[85,157,158],{"class":87,"line":113},[85,159,160],{},"    TOWNSHIP_CONVERT(legal_description):longitude::FLOAT AS longitude\n",[85,162,164],{"class":87,"line":163},6,[85,165,166],{},"FROM well_inventory\n",[85,168,170],{"class":87,"line":169},7,[85,171,172],{},"WHERE region = 'Cardium_Play';\n",[11,174,175,176,180],{},"This runs inline - no staging table, no intermediate step. For large-scale table enrichment (CTAS, LATERAL, Snowflake Tasks), the ",[60,177,179],{"href":178},"\u002Fblog\u002Fenrich-snowflake-table-dls-gps-coordinates","full enrichment walkthrough"," covers those patterns in detail.",[182,183,185],"h3",{"id":184},"define-a-view-for-on-demand-conversion","Define a View for On-Demand Conversion",[11,187,188],{},"If your source table updates continuously and you don't want to maintain a scheduled enrichment task, define a view that converts on read:",[76,190,192],{"className":78,"code":191,"language":80,"meta":81,"style":81},"CREATE OR REPLACE VIEW well_locations AS\nSELECT\n    well_id,\n    uwi,\n    legal_description,\n    TOWNSHIP_CONVERT(legal_description):latitude::FLOAT  AS latitude,\n    TOWNSHIP_CONVERT(legal_description):longitude::FLOAT AS longitude\nFROM well_inventory;\n",[15,193,194,199,203,207,212,216,220,224],{"__ignoreMap":81},[85,195,196],{"class":87,"line":88},[85,197,198],{},"CREATE OR REPLACE VIEW well_locations AS\n",[85,200,201],{"class":87,"line":94},[85,202,140],{},[85,204,205],{"class":87,"line":100},[85,206,145],{},[85,208,209],{"class":87,"line":107},[85,210,211],{},"    uwi,\n",[85,213,214],{"class":87,"line":113},[85,215,150],{},[85,217,218],{"class":87,"line":163},[85,219,155],{},[85,221,222],{"class":87,"line":169},[85,223,160],{},[85,225,227],{"class":87,"line":226},8,[85,228,229],{},"FROM well_inventory;\n",[11,231,232],{},"Dashboard tools that query this view always get coordinates fresh from the source. The trade-off is API call volume - every query that touches unconverted rows triggers conversions. For stable source data, a materialized table is more cost-efficient.",[11,234,235],{},[236,237],"img",{"alt":238,"src":239},"Batch-convert mixed legal land descriptions and review the results before piping coordinates into your warehouse","\u002Fimages\u002Fguides\u002Fbatch-conversion.webp",[31,241,243],{"id":242},"working-with-mixed-lld-formats","Working with Mixed LLD Formats",[11,245,246],{},"Most Snowflake enrichment guides assume a clean, single-format column. Real datasets rarely cooperate. A provincial well database might contain DLS quarter sections alongside Legal Subdivisions; an acquisition might introduce NTS references from BC assets. The same function resolves all of them:",[76,248,250],{"className":78,"code":249,"language":80,"meta":81,"style":81},"-- DLS quarter section (Alberta, Saskatchewan, Manitoba, BC Peace River block)\nSELECT TOWNSHIP_CONVERT('SE-36-42-3-W5'):latitude::FLOAT;\n\n-- Legal Subdivision - a 40-acre parcel within a DLS section\nSELECT TOWNSHIP_CONVERT('LSD 06-32-048-07W5'):latitude::FLOAT;\n",[15,251,252,257,262,266,271],{"__ignoreMap":81},[85,253,254],{"class":87,"line":88},[85,255,256],{},"-- DLS quarter section (Alberta, Saskatchewan, Manitoba, BC Peace River block)\n",[85,258,259],{"class":87,"line":94},[85,260,261],{},"SELECT TOWNSHIP_CONVERT('SE-36-42-3-W5'):latitude::FLOAT;\n",[85,263,264],{"class":87,"line":100},[85,265,104],{"emptyLinePlaceholder":103},[85,267,268],{"class":87,"line":107},[85,269,270],{},"-- Legal Subdivision - a 40-acre parcel within a DLS section\n",[85,272,273],{"class":87,"line":113},[85,274,275],{},"SELECT TOWNSHIP_CONVERT('LSD 06-32-048-07W5'):latitude::FLOAT;\n",[11,277,278,279,283],{},"Pass NTS map sheet references - common in BC resource and environmental datasets - using the same function call. No format detection, no branching logic, no separate function per system. The ",[60,280,282],{"href":281},"\u002Fguides\u002Fdominion-land-survey-system","DLS system guide"," explains how DLS quarter sections and Legal Subdivisions relate to each other - useful if you're working with a mixed Alberta dataset for the first time.",[31,285,287],{"id":286},"validate-before-you-enrich","Validate Before You Enrich",[11,289,290],{},"This is the step most teams skip, and it causes the most downstream problems.",[11,292,293,294,297,298,301,302,305],{},"A legal land description that looks valid can still fail to convert. The most common cause is a transposed meridian - changing ",[15,295,296],{},"W5"," to ",[15,299,300],{},"W4"," in ",[15,303,304],{},"SE-36-42-3-W5"," moves the location nearly 200 kilometres east, into a different province entirely. When the function can't resolve a description, it returns null. That null propagates silently into your production table and every model downstream.",[11,307,308],{},"The Snowflake Native App includes validation functions that run in pure SQL before any enrichment:",[76,310,312],{"className":78,"code":311,"language":80,"meta":81,"style":81},"-- Find descriptions that won't convert before running the enrichment\nSELECT\n    legal_description,\n    CORE.VALIDATE_LLD(legal_description)  AS is_valid,\n    CORE.PARSE_LLD(legal_description)     AS parsed_components\nFROM well_inventory\nWHERE NOT CORE.VALIDATE_LLD(legal_description);\n",[15,313,314,319,323,327,332,337,341],{"__ignoreMap":81},[85,315,316],{"class":87,"line":88},[85,317,318],{},"-- Find descriptions that won't convert before running the enrichment\n",[85,320,321],{"class":87,"line":94},[85,322,140],{},[85,324,325],{"class":87,"line":100},[85,326,150],{},[85,328,329],{"class":87,"line":107},[85,330,331],{},"    CORE.VALIDATE_LLD(legal_description)  AS is_valid,\n",[85,333,334],{"class":87,"line":113},[85,335,336],{},"    CORE.PARSE_LLD(legal_description)     AS parsed_components\n",[85,338,339],{"class":87,"line":163},[85,340,166],{},[85,342,343],{"class":87,"line":169},[85,344,345],{},"WHERE NOT CORE.VALIDATE_LLD(legal_description);\n",[11,347,348,351,352,355,356,359,360,363],{},[15,349,350],{},"CORE.VALIDATE_LLD"," returns ",[15,353,354],{},"true"," or ",[15,357,358],{},"false",". ",[15,361,362],{},"CORE.PARSE_LLD"," returns a structured JSON object with the individual components - direction, section, township, range, meridian - so you can see exactly which part of the description is malformed.",[11,365,366],{},"Common finds in O&G data:",[368,369,370,385,394],"ul",{},[371,372,373,376,377,380,381,384],"li",{},[41,374,375],{},"Trailing suffix from Alberta Land Titles",": ",[15,378,379],{},"SE-36-42-3-W5M"," - the ",[15,382,383],{},"M"," is a Meridian suffix added by some government export tools, not standard DLS notation",[371,386,387,376,390,393],{},[41,388,389],{},"Missing separator",[15,391,392],{},"NW2524-1-W5"," - looks like a DLS quarter section but won't parse",[371,395,396,399,400,403],{},[41,397,398],{},"Non-standard LSD format",": some AER exports omit the ",[15,401,402],{},"LSD"," prefix entirely, which can trip up downstream parsers even when the API resolves it correctly",[11,405,406],{},"Fix the bad rows in your source table first, then run the enrichment. One invalid description out of 15,000 costs nothing to fix before the CTAS runs; it costs real time to find and correct after that row has propagated through five dbt models.",[31,408,410],{"id":409},"running-conversion-in-a-dbt-model","Running Conversion in a dbt Model",[11,412,413],{},"If your warehouse uses dbt, the conversion integrates cleanly as an enrichment model:",[76,415,417],{"className":78,"code":416,"language":80,"meta":81,"style":81},"-- models\u002Fenriched\u002Fwell_locations.sql\n{{ config(materialized='table') }}\n\nSELECT\n    well_id,\n    uwi,\n    legal_description,\n    TOWNSHIP_CONVERT(legal_description):latitude::FLOAT  AS latitude,\n    TOWNSHIP_CONVERT(legal_description):longitude::FLOAT AS longitude,\n    CURRENT_TIMESTAMP() AS enriched_at\nFROM {{ ref('stg_well_inventory') }}\nWHERE legal_description IS NOT NULL\n  AND legal_description != ''\n",[15,418,419,424,429,433,437,441,445,449,453,459,465,471,477],{"__ignoreMap":81},[85,420,421],{"class":87,"line":88},[85,422,423],{},"-- models\u002Fenriched\u002Fwell_locations.sql\n",[85,425,426],{"class":87,"line":94},[85,427,428],{},"{{ config(materialized='table') }}\n",[85,430,431],{"class":87,"line":100},[85,432,104],{"emptyLinePlaceholder":103},[85,434,435],{"class":87,"line":107},[85,436,140],{},[85,438,439],{"class":87,"line":113},[85,440,145],{},[85,442,443],{"class":87,"line":163},[85,444,211],{},[85,446,447],{"class":87,"line":169},[85,448,150],{},[85,450,451],{"class":87,"line":226},[85,452,155],{},[85,454,456],{"class":87,"line":455},9,[85,457,458],{},"    TOWNSHIP_CONVERT(legal_description):longitude::FLOAT AS longitude,\n",[85,460,462],{"class":87,"line":461},10,[85,463,464],{},"    CURRENT_TIMESTAMP() AS enriched_at\n",[85,466,468],{"class":87,"line":467},11,[85,469,470],{},"FROM {{ ref('stg_well_inventory') }}\n",[85,472,474],{"class":87,"line":473},12,[85,475,476],{},"WHERE legal_description IS NOT NULL\n",[85,478,480],{"class":87,"line":479},13,[85,481,482],{},"  AND legal_description != ''\n",[11,484,485],{},"For incremental runs, filter to only rows that haven't been enriched:",[76,487,489],{"className":78,"code":488,"language":80,"meta":81,"style":81},"{{ config(materialized='incremental') }}\n\n...\n{% if is_incremental() %}\nWHERE legal_description NOT IN (SELECT legal_description FROM {{ this }})\n{% endif %}\n",[15,490,491,496,500,505,510,515],{"__ignoreMap":81},[85,492,493],{"class":87,"line":88},[85,494,495],{},"{{ config(materialized='incremental') }}\n",[85,497,498],{"class":87,"line":94},[85,499,104],{"emptyLinePlaceholder":103},[85,501,502],{"class":87,"line":100},[85,503,504],{},"...\n",[85,506,507],{"class":87,"line":107},[85,508,509],{},"{% if is_incremental() %}\n",[85,511,512],{"class":87,"line":113},[85,513,514],{},"WHERE legal_description NOT IN (SELECT legal_description FROM {{ this }})\n",[85,516,517],{"class":87,"line":163},[85,518,519],{},"{% endif %}\n",[11,521,522],{},"This avoids re-calling the API for rows already in the enriched table - important for keeping costs predictable at the Scale and Enterprise tiers. The API charges per request, not per row per run.",[31,524,526],{"id":525},"get-started","Get Started",[11,528,529],{},[236,530],{"alt":531,"src":532},"Township Canada interactive map preview for verifying converted Snowflake rows against parcel boundaries","\u002Fimages\u002Fguides\u002Finteractive-map.webp",[11,534,535,536,540],{},"Both options require a Township Canada API key. Sign up on the ",[60,537,539],{"href":538},"\u002Fapi","API page"," - the Batch API starts at $40\u002Fmonth for 1,000 requests.",[368,542,543,553],{},[371,544,545,548,549,552],{},[41,546,547],{},"Native App",": Install from the Snowflake Marketplace, connect your API key in the Settings panel, and call ",[15,550,551],{},"TOWNSHIP_CANADA_CONVERT()"," plus the validation functions immediately.",[371,554,555,558,559,561],{},[41,556,557],{},"External Function",": Follow the ",[60,560,63],{"href":62}," - Lambda, API Gateway, and Snowflake integration configuration all in one place.",[11,563,564,565,569],{},"The ",[60,566,568],{"href":567},"\u002Fblog\u002Fsnowflake-dls-enrichment","Snowflake DLS enrichment post"," goes deeper on the business case for warehouse-native conversion and covers the Databricks UDF variant if your team runs on Databricks.",[11,571,572],{},"For API subscribers already using Township Canada for batch conversion via the web app, the Snowflake Native App connects to the same API key - no separate subscription.",[574,575,576],"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);}",{"title":81,"searchDepth":94,"depth":94,"links":578},[579,580,583,584,585,586],{"id":33,"depth":94,"text":34},{"id":70,"depth":94,"text":71,"children":581},[582],{"id":184,"depth":100,"text":185},{"id":242,"depth":94,"text":243},{"id":286,"depth":94,"text":287},{"id":409,"depth":94,"text":410},{"id":525,"depth":94,"text":526},"guides","https:\u002F\u002Fb9bukyyl5yuyveqq.public.blob.vercel-storage.com\u002Fimages\u002Fblog\u002F2026-04\u002Fcd45f664-b792-4f38-a3e7-a9e162a3db65.jpeg","2026-04-17","Convert Canadian DLS, LSD, and NTS legal land descriptions to GPS coordinates in Snowflake SQL - validate bad data first, enrich tables inline, and keep your pipeline clean.","md",{},"\u002Fblog\u002Fconvert-legal-land-descriptions-snowflake-sql",{"title":5,"description":590},"blog\u002Fconvert-legal-land-descriptions-snowflake-sql",[597,598,599,600,601],"Snowflake","SQL","API","Oil and Gas","Developer","ydIMNUpehHhznMGhusRcqy4gxrJLAzz-nr1CtJGQ7S8"]