[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-\u002Fblog\u002Foklahoma-plss-to-gps-power-bi-excel-google-sheets":3},{"id":4,"title":5,"body":6,"cover":626,"date":627,"description":628,"draft":629,"extension":630,"meta":631,"navigation":650,"path":651,"seo":652,"stem":653,"tags":654,"__hash__":661},"blog\u002Fblog\u002Foklahoma-plss-to-gps-power-bi-excel-google-sheets.md","Convert Oklahoma PLSS to Lat\u002FLong in Power BI & Excel",{"type":7,"value":8,"toc":617},"minimark",[9,18,21,26,33,49,53,56,188,213,216,220,227,238,514,526,530,539,547,550,554,561,564,568,578,592,596,599,613],[10,11,12,13,17],"p",{},"A production team hands you an Oklahoma well table. Every row carries a legal description like ",[14,15,16],"strong",{},"T16N R8W Sec 14, Indian Meridian"," — clean, precise, and completely unreadable to Power BI. Drop that column onto a Map visual and you get nothing, because Power BI geocodes place names and latitude\u002Flongitude, not Public Land Survey System grid references.",[10,19,20],{},"This is the gap O&G data teams hit when they try to convert Oklahoma Section-Township-Range to lat\u002Flong for Power BI. The wells live in PLSS, the dashboard needs coordinates, and there's no built-in step between them. Here is the workflow that closes that gap — in Power BI, Excel, and Google Sheets — and how to run it across a few thousand rows without converting them by hand.",[22,23,25],"h2",{"id":24},"why-the-legal-description-wont-plot","Why the legal description won't plot",[10,27,28,29,32],{},"A PLSS description is a position on a survey grid, counted from a principal meridian's initial point. ",[14,30,31],{},"T16N R8W Sec 14"," means Township 16 North, Range 8 West, Section 14 — but that grid coordinate only resolves to a place once you know the meridian. Read it under the Indian Meridian and you're in Kingfisher County's STACK play; the same Township and Range under a different meridian is somewhere else entirely.",[10,34,35,36,40,41,48],{},"Power BI has no concept of any of this. So the fix is a geocoding step that runs ",[37,38,39],"em",{},"before"," the visual: legal description in, latitude\u002Flongitude out, joined back onto the well table. The ",[42,43,47],"a",{"href":44,"rel":45},"https:\u002F\u002Ftownshipamerica.com\u002Fapi",[46],"nofollow","Township America API"," does that conversion against BLM survey data and returns GeoJSON, which every one of these tools can parse.",[22,50,52],{"id":51},"power-bi-geocode-in-power-query","Power BI: geocode in Power Query",[10,54,55],{},"Power BI's data layer is Power Query, and Power Query can call a REST endpoint during refresh. That makes it the natural place to convert the legal-description column. Send the column to the batch endpoint, then expand the returned coordinates into two new columns.",[57,58,63],"pre",{"className":59,"code":60,"language":61,"meta":62,"style":62},"language-powerquery-m shiki shiki-themes material-theme-lighter github-light github-dark","let\n    Wells = Source[Data],\n    Descriptions = Table.Column(Wells, \"LegalDescription\"),\n    Body = Json.FromValue([descriptions = Descriptions]),\n    Response = Web.Contents(\n        \"https:\u002F\u002Fapi.townshipamerica.com\u002Fv1\u002Fbatch\u002Fsearch\",\n        [\n            Headers = [#\"Authorization\" = \"Bearer \" & ApiKey,\n                       #\"Content-Type\" = \"application\u002Fjson\"],\n            Content = Body\n        ]\n    ),\n    Features = Json.Document(Response)[features],\n    Coords = Table.FromList(Features, each {\n        _[properties][description],\n        _[geometry][coordinates]{1},   \u002F\u002F latitude\n        _[geometry][coordinates]{0}    \u002F\u002F longitude\n    }, {\"LegalDescription\", \"Latitude\", \"Longitude\"})\nin\n    Coords\n","powerquery-m","",[64,65,66,74,80,86,92,98,104,110,116,122,128,134,140,146,152,158,164,170,176,182],"code",{"__ignoreMap":62},[67,68,71],"span",{"class":69,"line":70},"line",1,[67,72,73],{},"let\n",[67,75,77],{"class":69,"line":76},2,[67,78,79],{},"    Wells = Source[Data],\n",[67,81,83],{"class":69,"line":82},3,[67,84,85],{},"    Descriptions = Table.Column(Wells, \"LegalDescription\"),\n",[67,87,89],{"class":69,"line":88},4,[67,90,91],{},"    Body = Json.FromValue([descriptions = Descriptions]),\n",[67,93,95],{"class":69,"line":94},5,[67,96,97],{},"    Response = Web.Contents(\n",[67,99,101],{"class":69,"line":100},6,[67,102,103],{},"        \"https:\u002F\u002Fapi.townshipamerica.com\u002Fv1\u002Fbatch\u002Fsearch\",\n",[67,105,107],{"class":69,"line":106},7,[67,108,109],{},"        [\n",[67,111,113],{"class":69,"line":112},8,[67,114,115],{},"            Headers = [#\"Authorization\" = \"Bearer \" & ApiKey,\n",[67,117,119],{"class":69,"line":118},9,[67,120,121],{},"                       #\"Content-Type\" = \"application\u002Fjson\"],\n",[67,123,125],{"class":69,"line":124},10,[67,126,127],{},"            Content = Body\n",[67,129,131],{"class":69,"line":130},11,[67,132,133],{},"        ]\n",[67,135,137],{"class":69,"line":136},12,[67,138,139],{},"    ),\n",[67,141,143],{"class":69,"line":142},13,[67,144,145],{},"    Features = Json.Document(Response)[features],\n",[67,147,149],{"class":69,"line":148},14,[67,150,151],{},"    Coords = Table.FromList(Features, each {\n",[67,153,155],{"class":69,"line":154},15,[67,156,157],{},"        _[properties][description],\n",[67,159,161],{"class":69,"line":160},16,[67,162,163],{},"        _[geometry][coordinates]{1},   \u002F\u002F latitude\n",[67,165,167],{"class":69,"line":166},17,[67,168,169],{},"        _[geometry][coordinates]{0}    \u002F\u002F longitude\n",[67,171,173],{"class":69,"line":172},18,[67,174,175],{},"    }, {\"LegalDescription\", \"Latitude\", \"Longitude\"})\n",[67,177,179],{"class":69,"line":178},19,[67,180,181],{},"in\n",[67,183,185],{"class":69,"line":184},20,[67,186,187],{},"    Coords\n",[10,189,190,191,194,195,198,199,202,203,206,207,212],{},"Merge ",[64,192,193],{},"Coords"," back onto your well table on ",[64,196,197],{},"LegalDescription",", and point a Map or Azure Maps visual at the new ",[64,200,201],{},"Latitude"," and ",[64,204,205],{},"Longitude"," fields. Because the call lives in the query, a scheduled dataset refresh re-geocodes any new wells automatically — no manual export, no copy-paste. For the single-lookup case while you're building the query, the ",[42,208,211],{"href":209,"rel":210},"https:\u002F\u002Ftownshipamerica.com\u002Foklahoma-plss-converter",[46],"Oklahoma PLSS converter"," confirms a coordinate by eye before you wire up the batch call.",[10,214,215],{},"![Power Query flow geocoding an Oklahoma well table: legal-description column to the batch API to Latitude and Longitude columns feeding a Power BI map](PLACEHOLDER: diagram-flow — PLSS column → Township America batch API → lat\u002Flong columns → Power BI map of Oklahoma wells, navy and amber)",[22,217,219],{"id":218},"excel-same-engine-an-office-script-for-one-offs","Excel: same engine, an Office Script for one-offs",[10,221,222,223,226],{},"Excel runs the same Power Query engine, so the M query above works unchanged inside Excel's ",[14,224,225],{},"Data → Get Data → Blank Query",". For a full well sheet, that's the path to take — it handles thousands of rows and refreshes on demand.",[10,228,229,230,233,234,237],{},"For an ad-hoc single conversion you might reach for a formula, but Excel has no native function that parses a JSON response: ",[64,231,232],{},"WEBSERVICE"," only pairs with ",[64,235,236],{},"FILTERXML",", which expects XML, not the GeoJSON the endpoint returns. The one-cell path that actually works is a short Office Script that calls the search endpoint and writes the coordinate next to the description:",[57,239,243],{"className":240,"code":241,"language":242,"meta":62,"style":62},"language-typescript shiki shiki-themes material-theme-lighter github-light github-dark","async function main(workbook: ExcelScript.Workbook) {\n  const cell = workbook.getActiveCell();\n  const desc = cell.getValue() as string;\n  const res = await fetch(\n    `https:\u002F\u002Fapi.townshipamerica.com\u002Fv1\u002Fsearch?q=${encodeURIComponent(desc)}`\n  );\n  const [lng, lat] = (await res.json()).features[0].geometry.coordinates;\n  cell.getOffsetRange(0, 1).setValue(`${lat}, ${lng}`);\n}\n","typescript",[64,244,245,286,314,343,361,388,395,458,509],{"__ignoreMap":62},[67,246,247,251,254,258,262,266,270,274,277,280,283],{"class":69,"line":70},[67,248,250],{"class":249},"sbsja","async",[67,252,253],{"class":249}," function",[67,255,257],{"class":256},"sGLFI"," main",[67,259,261],{"class":260},"sP7_E","(",[67,263,265],{"class":264},"s99_P","workbook",[67,267,269],{"class":268},"smGrS",":",[67,271,273],{"class":272},"sbgvK"," ExcelScript",[67,275,276],{"class":260},".",[67,278,279],{"class":272},"Workbook",[67,281,282],{"class":260},")",[67,284,285],{"class":260}," {\n",[67,287,288,291,295,298,302,304,307,311],{"class":69,"line":76},[67,289,290],{"class":249},"  const",[67,292,294],{"class":293},"s_hVV"," cell",[67,296,297],{"class":268}," =",[67,299,301],{"class":300},"su5hD"," workbook",[67,303,276],{"class":260},[67,305,306],{"class":256},"getActiveCell",[67,308,310],{"class":309},"skxfh","()",[67,312,313],{"class":260},";\n",[67,315,316,318,321,323,325,327,330,333,337,341],{"class":69,"line":82},[67,317,290],{"class":249},[67,319,320],{"class":293}," desc",[67,322,297],{"class":268},[67,324,294],{"class":300},[67,326,276],{"class":260},[67,328,329],{"class":256},"getValue",[67,331,332],{"class":309},"() ",[67,334,336],{"class":335},"sVHd0","as",[67,338,340],{"class":339},"sZMiF"," string",[67,342,313],{"class":260},[67,344,345,347,350,352,355,358],{"class":69,"line":88},[67,346,290],{"class":249},[67,348,349],{"class":293}," res",[67,351,297],{"class":268},[67,353,354],{"class":335}," await",[67,356,357],{"class":256}," fetch",[67,359,360],{"class":309},"(\n",[67,362,363,367,371,374,377,380,383,385],{"class":69,"line":94},[67,364,366],{"class":365},"sjJ54","    `",[67,368,370],{"class":369},"s_sjI","https:\u002F\u002Fapi.townshipamerica.com\u002Fv1\u002Fsearch?q=",[67,372,373],{"class":365},"${",[67,375,376],{"class":256},"encodeURIComponent",[67,378,261],{"class":379},"sfo-9",[67,381,382],{"class":300},"desc",[67,384,282],{"class":379},[67,386,387],{"class":365},"}`\n",[67,389,390,393],{"class":69,"line":100},[67,391,392],{"class":309},"  )",[67,394,313],{"class":260},[67,396,397,399,402,405,408,411,414,416,419,422,424,426,429,432,434,437,440,444,446,448,451,453,456],{"class":69,"line":106},[67,398,290],{"class":249},[67,400,401],{"class":260}," [",[67,403,404],{"class":293},"lng",[67,406,407],{"class":260},",",[67,409,410],{"class":293}," lat",[67,412,413],{"class":260},"]",[67,415,297],{"class":268},[67,417,418],{"class":309}," (",[67,420,421],{"class":335},"await",[67,423,349],{"class":300},[67,425,276],{"class":260},[67,427,428],{"class":256},"json",[67,430,431],{"class":309},"())",[67,433,276],{"class":260},[67,435,436],{"class":300},"features",[67,438,439],{"class":309},"[",[67,441,443],{"class":442},"srdBf","0",[67,445,413],{"class":309},[67,447,276],{"class":260},[67,449,450],{"class":300},"geometry",[67,452,276],{"class":260},[67,454,455],{"class":300},"coordinates",[67,457,313],{"class":260},[67,459,460,463,465,468,470,472,474,477,479,481,484,486,489,492,495,498,500,502,505,507],{"class":69,"line":112},[67,461,462],{"class":300},"  cell",[67,464,276],{"class":260},[67,466,467],{"class":256},"getOffsetRange",[67,469,261],{"class":309},[67,471,443],{"class":442},[67,473,407],{"class":260},[67,475,476],{"class":442}," 1",[67,478,282],{"class":309},[67,480,276],{"class":260},[67,482,483],{"class":256},"setValue",[67,485,261],{"class":309},[67,487,488],{"class":365},"`${",[67,490,491],{"class":300},"lat",[67,493,494],{"class":365},"}",[67,496,497],{"class":369},", ",[67,499,373],{"class":365},[67,501,404],{"class":300},[67,503,504],{"class":365},"}`",[67,506,282],{"class":309},[67,508,313],{"class":260},[67,510,511],{"class":69,"line":118},[67,512,513],{"class":260},"}\n",[10,515,516,517,519,520,525],{},"Select the cell holding ",[14,518,16],{},", run the script, and the latitude\u002Flongitude lands in the next column. Keep the script for spot checks and use Power Query for the table — the same ",[42,521,524],{"href":522,"rel":523},"https:\u002F\u002Ftownshipamerica.com\u002Fhow-to\u002Fbatch-convert-plss-descriptions",[46],"batch conversion workflow"," backs both.",[22,527,529],{"id":528},"google-sheets-a-column-function","Google Sheets: a column function",[10,531,532,533,538],{},"Google Sheets has no Power Query, so the conversion runs through the Township America ",[42,534,537],{"href":535,"rel":536},"https:\u002F\u002Ftownshipamerica.com\u002Fguides\u002Fgoogle-sheets-add-on",[46],"Google Sheets add-on",". Once installed, it registers a custom function you fill down a column:",[57,540,545],{"className":541,"code":543,"language":544},[542],"language-text","=TA_TO_LATLONG(A2)\n","text",[64,546,543],{"__ignoreMap":62},[10,548,549],{},"With your legal descriptions in column A, drop that formula in B2, fill it down, and each row resolves to its latitude\u002Flongitude. It's the right fit for a smaller lease workbook a land team keeps in Sheets, where standing up a Power BI dataset would be more machinery than the job needs.",[22,551,553],{"id":552},"performance-convert-the-whole-table-in-minutes","Performance: convert the whole table in minutes",[10,555,556,557,560],{},"The reason this holds up on production data is the batch endpoint. It accepts ",[14,558,559],{},"up to 100 PLSS descriptions per request"," and returns all 100 coordinates in a single GeoJSON response, typically in under 200 milliseconds. A 5,000-row Oklahoma well table is 50 calls — done in well under a minute, not the afternoon a manual lookup would cost.",[10,562,563],{},"That headroom matters when a table spans meridians. An Oklahoma dataset that reaches into the panhandle mixes the Indian Meridian with the Cimarron Meridian, and a multi-state O&G portfolio adds the 6th Principal Meridian and others. The batch endpoint carries each description's meridian through the conversion, so you don't pre-sort the table by survey system. Sub-meter centroid accuracy on every row means the pins land in the right Section, not a grid-formula approximation.",[22,565,567],{"id":566},"worked-example-a-scoopstack-production-dashboard","Worked example: a SCOOP\u002FSTACK production dashboard",[10,569,570,571,573,574,577],{},"Say you're building a production dashboard for Oklahoma's SCOOP and STACK plays. The well master arrives as PLSS — ",[14,572,16],{}," in the Kingfisher County STACK, ",[14,575,576],{},"T7N R6W Sec 22, Indian Meridian"," down in the Grady County SCOOP — joined to monthly volumes from the state corporation commission.",[10,579,580,581,202,583,585,586,591],{},"The pipeline is three steps. Power Query pulls the well master and pipes the legal-description column through the batch endpoint, adding ",[64,582,201],{},[64,584,205],{},". The production table joins on the well's API number. An Azure Maps visual plots each well, sized by cumulative production and colored by operator. Because the geocoding lives in the refresh, next month's new permits flow onto the map the moment volumes land — no one re-runs a conversion. To enrich the same sections with permit and operator context, the ",[42,587,590],{"href":588,"rel":589},"https:\u002F\u002Ftownshipamerica.com\u002Foklahoma-oil-gas-wells",[46],"Oklahoma oil & gas well data"," pages return wells by legal description for the exact Sections your dashboard maps.",[22,593,595],{"id":594},"wire-it-into-your-stack","Wire it into your stack",[10,597,598],{},"Converting Oklahoma Section-Township-Range to lat\u002Flong for Power BI is a data-prep step, not a manual chore — one API call in the query that loads your model, and the map takes care of itself. The same call works in Excel and Google Sheets, so a team can standardize on one conversion source across every tool it already uses.",[10,600,601,602,606,607,612],{},"See the ",[42,603,605],{"href":44,"rel":604},[46],"API docs"," for the single and batch endpoints — both return GeoJSON you can parse directly in Power Query. If your dashboards need the parcel as a polygon rather than a center point, ",[42,608,611],{"href":609,"rel":610},"https:\u002F\u002Ftownshipamerica.com\u002Fpricing",[46],"GIS-ready PLSS exports on Business"," return full Section and quarter-section boundaries as GeoJSON, Shapefile, or KMZ.",[614,615,616],"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 .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sfo-9, html code.shiki .sfo-9{--shiki-light:#90A4AE;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":62,"searchDepth":76,"depth":76,"links":618},[619,620,621,622,623,624,625],{"id":24,"depth":76,"text":25},{"id":51,"depth":76,"text":52},{"id":218,"depth":76,"text":219},{"id":528,"depth":76,"text":529},{"id":552,"depth":76,"text":553},{"id":566,"depth":76,"text":567},{"id":594,"depth":76,"text":595},"https:\u002F\u002Fhswwubqgpp6hnvaz.public.blob.vercel-storage.com\u002Fimages\u002Fblog\u002F2026-06\u002F509a66af-dcde-45f2-9c1d-a5f761b7f177.jpeg","2026-06-05T10:00:00Z","Convert Oklahoma Section-Township-Range to lat\u002Flong for Power BI, Excel, and Google Sheets — a batch geocoding workflow for O&G well tables, with code.",false,"md",{"author":632,"category":633,"faqs":634},"Township America","guide",[635,638,641,644,647],{"question":636,"answer":637},"How do I convert Oklahoma Section-Township-Range to lat\u002Flong for Power BI?","Power BI does not read PLSS legal descriptions, so you geocode them before the map visual loads. In Power Query, send your legal-description column to the Township America batch endpoint and parse the returned GeoJSON into Latitude and Longitude columns. Point the Map or Azure Map visual at those two columns and the wells plot correctly.",{"question":639,"answer":640},"Why doesn't Power BI map a description like T16N R8W Sec 14, Indian Meridian?","Power BI's map visuals expect latitude and longitude, or a place name its geocoder recognizes. A PLSS legal description is neither — it's a survey grid reference measured from a principal meridian. You have to convert it to coordinates first.",{"question":642,"answer":643},"Can I convert a whole Oklahoma well table at once?","Yes. The batch endpoint accepts up to 100 PLSS descriptions per request and returns GeoJSON in one response, so a several-thousand-row well table converts in a few dozen calls — fast enough to refresh inside a scheduled Power BI dataset refresh.",{"question":645,"answer":646},"Does this work in Excel and Google Sheets too?","Yes. Excel uses the same Power Query engine as Power BI, so the M query is identical. Google Sheets uses the Township America add-on, which adds a custom function you can fill down a column of legal descriptions.",{"question":648,"answer":649},"Which Oklahoma meridian do I use?","Most of Oklahoma is surveyed from the Indian Meridian. The panhandle (Cimarron, Texas, and Beaver counties) uses the Cimarron Meridian. Naming the wrong meridian points the same Township and Range at a different place, so include it in every description you send.",true,"\u002Fblog\u002Foklahoma-plss-to-gps-power-bi-excel-google-sheets",{"title":5,"description":628},"blog\u002Foklahoma-plss-to-gps-power-bi-excel-google-sheets",[655,656,657,658,659,660],"PLSS","Power BI","Excel","Oklahoma","Developer","GIS","XHJ-w0nezjEigWelgEBhiABjWyK00J37dfq7HcpBvCI"]