30. 4. 2026

Makro v Excelu: Doplnění PSČ podle adresy s využitím API Mapy.com

Dostal jsem zajímavý úkol v Exelu:K neúplným adresám doplit chybějící PSČ obce.

Výslední excel měl vypadat takto:


Vstupní data byla ale jen částečná, napřklad chyběla ulice, nebo PSČ

Do chybějící ulie jsem tedy doplnil název institutu a sestavil makro, které pomocí API mapy.com poplní PSČ. K použití API je potřeba získat z https://developer.mapy.com/cs/rest-api/jak-zacit/ API klíč.

Celý modul ve VBA pak vypadá následovně:

Option Explicit

Const MAPY_API_KEY As String = "MujAPIklicDevelopera"

' --- UTF-8 URL-encode pomocí ADODB.Stream ---
Function URLEncodeUTF8(s As String) As String
    Dim stm As Object
    Dim bytes() As Byte
    Dim i As Long
    Dim ch As Long
    Dim out As String

    Set stm = CreateObject("ADODB.Stream")
    stm.Type = 2 ' text
    stm.Charset = "utf-8"
    stm.Open
    stm.WriteText s
    stm.Position = 0
    stm.Type = 1 ' binary
    bytes = stm.Read
    stm.Close

    out = ""
    For i = 0 To UBound(bytes)
        ch = bytes(i)
        If (ch >= 48 And ch <= 57) Or (ch >= 65 And ch <= 90) Or (ch >= 97 And ch <= 122) Or ch = 45 Or ch = 46 Or ch = 95 Or ch = 126 Then
            out = out & Chr(ch)
        Else
            out = out & "%" & Right("0" & Hex(ch), 2)
        End If
    Next i

    URLEncodeUTF8 = out
End Function

' --- Extrakce zip pomocí regulárního výrazu ---
Function ExtractZipFromJson(json As String) As String
    Dim re As Object
    Dim matches As Object
    Dim candidate As String

    Set re = CreateObject("VBScript.RegExp")
    re.Pattern = """zip""\s*:\s*""([^""]+)"""
    re.IgnoreCase = True
    re.Global = False

    If re.Test(json) Then
        Set matches = re.Execute(json)
        candidate = Trim(matches(0).SubMatches(0))
        candidate = Replace(candidate, vbTab, " ")
        candidate = Replace(candidate, vbCr, "")
        candidate = Replace(candidate, vbLf, "")
        candidate = Trim(candidate)
        If Len(candidate) = 5 And InStr(candidate, " ") = 0 Then
            candidate = Left(candidate, 3) & " " & Right(candidate, 2)
        End If
        ExtractZipFromJson = candidate
    Else
        ExtractZipFromJson = ""
    End If
End Function

' --- Testovací Sub pro ověření volání API a dekódování UTF-8 ---
Sub Test_MapyCZ_Request()
    Dim http As Object
    Dim url As String
    Dim adresa As String
    Dim encoded As String
    Dim bytes() As Byte
    Dim stm As Object
    Dim response As String
    Dim statusCode As Long
    Dim zip As String

    On Error GoTo ErrHandler

    adresa = "Křižíkova 188 Brno" ' změň podle potřeby
    encoded = URLEncodeUTF8(adresa)

    url = "https://api.mapy.cz/v1/suggest?apikey=" & MAPY_API_KEY & "&limit=1&query=" & encoded

    Set http = CreateObject("MSXML2.ServerXMLHTTP.6.0")
    http.Open "GET", url, False
    http.setRequestHeader "User-Agent", "ExcelVBA"
    http.send

    statusCode = http.Status
    If statusCode <> 200 Then
        ThisWorkbook.Sheets(1).Range("Z1").Value = "HTTP Status: " & statusCode & " - " & http.statusText
        Exit Sub
    End If

    bytes = http.responseBody

    ' Převod raw bytes (UTF-8) na text pomocí ADODB.Stream
    Set stm = CreateObject("ADODB.Stream")
    stm.Type = 1 ' binary
    stm.Open
    stm.Write bytes
    stm.Position = 0
    stm.Type = 2 ' text
    stm.Charset = "utf-8"
    response = stm.ReadText
    stm.Close

    ThisWorkbook.Sheets(1).Range("Z1").Value = response

    zip = ExtractZipFromJson(response)
    If zip <> "" Then
        ThisWorkbook.Sheets(1).Range("Z2").Value = zip
    Else
        ThisWorkbook.Sheets(1).Range("Z2").Value = "zip not found"
    End If

    MsgBox "Hotovo. Podívej se do Z1 (raw JSON) a Z2 (zip).", vbInformation
    Exit Sub

ErrHandler:
    ThisWorkbook.Sheets(1).Range("Z1").Value = "CHYBA: " & Err.Number & " - " & Err.Description
    MsgBox "Chyba: " & Err.Description, vbExclamation
End Sub

' --- Hlavní sub pro hromadné doplnění PSČ (pouze C,D -> E), bez LOGu ---
Sub DoplnitPSC_Hromadne()
    Dim ws As Worksheet
    Dim lastRow As Long, i As Long
    Dim ulice As String, mesto As String, adresa As String
    Dim encoded As String, url As String
    Dim http As Object
    Dim bytes() As Byte, response As String
    Dim zip As String
    Dim stm As Object
    Dim statusCode As Long
    Dim pauseMs As Long
    Dim processed As Long, skipped As Long
    Dim startRow As Long

    ' Nastavení
    pauseMs = 200    ' pauza mezi dotazy v ms
    startRow = 2     ' uprav pokud máš jiný počet hlaviček
    Set ws = ThisWorkbook.Sheets(1) ' změň na Worksheets("NázevListu") pokud potřebuješ

    lastRow = ws.Cells(ws.Rows.Count, "C").End(xlUp).Row
    processed = 0: skipped = 0

    For i = startRow To lastRow
        ' pracujeme výhradně se sloupci C (ulice), D (město), E (psc)
        ulice = Trim(ws.Cells(i, "C").Value)
        mesto = Trim(ws.Cells(i, "D").Value)

        ' Přeskočit prázdné řádky (C i D prázdné)
        If ulice = "" And mesto = "" Then
            skipped = skipped + 1
            GoTo NextRow
        End If

        ' Přeskočit, pokud E už obsahuje platnou hodnotu (nechceme přepisovat)
        If Trim(ws.Cells(i, "E").Value) <> "" And _
           Trim(ws.Cells(i, "E").Value) <> "0" And _
           LCase(Trim(ws.Cells(i, "E").Value)) <> "n" And _
           LCase(Trim(ws.Cells(i, "E").Value)) <> "není" Then
            skipped = skipped + 1
            GoTo NextRow
        End If

        ' Přeskočit řádky, kde C vypadá jako JSON nebo obsahuje "items"
        If Left(ulice, 1) = "{" Or InStr(1, ulice, "items", vbTextCompare) > 0 Then
            skipped = skipped + 1
            GoTo NextRow
        End If

        ' Sestav adresu pouze z C a D
        adresa = ulice
        If mesto <> "" Then adresa = adresa & ", " & mesto

        ' Volání API
        encoded = URLEncodeUTF8(adresa)
        url = "https://api.mapy.cz/v1/suggest?apikey=" & MAPY_API_KEY & "&limit=1&query=" & encoded

        Set http = CreateObject("MSXML2.ServerXMLHTTP.6.0")
        On Error Resume Next
        http.Open "GET", url, False
        http.setRequestHeader "User-Agent", "ExcelVBA"
        http.send
        On Error GoTo 0

        On Error GoTo RequestError
        statusCode = http.Status

        If statusCode <> 200 Then
            ws.Cells(i, "E").Value = "?"
            GoTo PauseAndContinue
        End If

        bytes = http.responseBody

        ' Převod raw bytes (UTF-8) na text pomocí ADODB.Stream
        Set stm = CreateObject("ADODB.Stream")
        stm.Type = 1 ' binary
        stm.Open
        stm.Write bytes
        stm.Position = 0
        stm.Type = 2 ' text
        stm.Charset = "utf-8"
        response = stm.ReadText
        stm.Close

        ' Extrahuj zip
        zip = ExtractZipFromJson(response)
        If zip <> "" Then
            ws.Cells(i, "E").Value = zip
        Else
            ws.Cells(i, "E").Value = "?"
        End If
        processed = processed + 1

PauseAndContinue:
        ' Pauza mezi dotazy
        If pauseMs > 0 Then
            Dim t0 As Double
            t0 = Timer
            Do While Timer < t0 + pauseMs / 1000
                DoEvents
            Loop
        End If

NextRow:
        ' pokračuj
    Next i

    MsgBox "Hotovo. Zapsáno: " & processed & " adres. Přeskočeno: " & skipped & ".", vbInformation
    Exit Sub

RequestError:
    ws.Cells(i, "E").Value = "?"
    Resume PauseAndContinue
End Sub



Žádné komentáře:

Okomentovat

K vkládání komentáře se můžete přihlásit bez registrace pomocí OpenID na Seznam.cz