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