Access

 MS Access. Baza miejscowości i ulic. Pliki pełne ( *.csv ) rejestru TERYT.

Krajowy Rejestr Urzędowy Podziału Terytorialnego Kraju (TERYT)

Zapis danych Rejestru TERYT do tabel MS Access

• Stan na dzień 03 marca 2018 roku

Główny Urząd Statystyczny (GUS) umożliwia pobranie z bazy Krajowego Rejestru Urzędowego Podziału Terytorialnego Kraju Pliki pełne rejestru TERYT. w formacie XML oraz CSV i kodowaniu UTF-8, poniższych katalogów:

  • TERC - system (zbiór) identyfikatorów i nazw jednostek podziału terytorialnego.
  • SIMC - system (zbiór) identyfikatorów (urzędowych) i nazw miejscowości.
  • ULIC - system (zbiór) identyfikatorów i nazwy ulic.
  • WMRODZ - zbiór (wykaz) symboli i nazw rodzajów miejscowości.

TERYT. Pełne pliki do pobrania
Rejestr TERYT. Pliki pełne do pobrania

Pobieramy cztery pliki TERC *.zip, SIMC *.zip, ULIC *.zip, WMRODZ *.zip. Po rozpakowaniu archiwum *.zip do katalogu TERYT otrzymamy poniższe pliki *.csv i *.xml. Interesują nas jedynie pliki *.csv.


Folder TERYT
Pobrane pliki pełne Rejestru TERYT
Znak Uwaga
  • Cztery pliki TERC *.csv, SIMC *.csv, ULIC *.csv, WMRODZ *.csv bezwzględnie muszą się znajdować w katalog TERYT
  • katalog TERYT musi znajdować się w katalogu bieżącej bazy danych
  • Nazwy plików muszą rozpoczynać się czteroznakowymi przedrostkami
    "TERC", "SIMC", "ULIC", "WMRO"*.csv.
  • W katalogu TERYT powinny się znajdować jedynie cztery pliki typu "CSV" o takich nazwach.

Przetwarzanie plików *.csv.

Czy pliki znajdują się dysku

Aby sprawdzić, czy pliki znajdują się na dysku skorzystamy z wbudowanej funkcji Dir. Kolejno będziemy pobierali nazwy plików z katalogu TERYT i sprawdzali, czy pierwsze cztery znaki nazwy pliku pasują do poniższych wzorców:
"TERC" "SIMC" "ULIC" "WMRO". Jeżeli przedrostek jest zgodny, pełna nazwa pliku jest przypisywana do odpowiedniej zmiennej. W przypadku obecności większej liczby plików z takim samym przedrostkiem pobrana zostanie nazwa ostatniego znalezionego pliku, ale należy się spodziewać błędów przy wczytywaniu danych do tabel.

...
	' utwórz ścieżkę do folderu TERYT z plikami pełnymi
	m_sAppPath = Application.CurrentProject.Path & "\TERYT\"

	' pobierz nazwy plików z folderu TERYT
	sFileCsv = Dir(m_sAppPath)

	Do Until sFileCsv = ""
		'tylko pliki z rozszerzeniem '.csv'
		If Right$(sFileCsv, 4) = ".csv" Then
			' rozpoznaj pliki po 4 pierwszych znakach
			Select Case Left$(sFileCsv, 4)
				Case "SIMC"
					sFileSIMC = sFileCsv
				Case "TERC"
					sFileTERC = sFileCsv
				Case "WMRO"
					sFileWMRODZ = sFileCsv
				Case "ULIC"
					sFileULIC = sFileCsv
				Case Else
			End Select
			Debug.Print sFileCsv
		End If
		sFileCsv = Dir
	Loop

	' muszą istnieć wszystkie pliki pełne rejestru TERYT
	If Len(sFileSIMC) = 0 Or Len(sFileTERC) = 0 Or _
		 Len(sFileWMRODZ) = 0 Or Len(sFileULIC) = 0 Then
		MsgBox "Katalog TERYT musi zawierać pliki:" & vbNewLine & _
					"  SIMC.csv" & vbNewLine & "  TERC.csv" & vbNewLine & _
					"  WMRODZ.csv" & vbNewLine & "  ULIC.csv", vbCritical
		Exit Sub
	End If
...

Usuwanie znacznika BOM.

Na początku każdego pobranego pliku *.csv znajduje się (może znajdować się) znacznik BOM (Byte Order Mark). Jest to niedrukowalny (niewidoczny) znak używany w wielobajtowym kodowaniu znaków, określający kolejności bajtów. Znacznik BOM zapisywany jest na początku pliku i dla plików UTF-8 ma postać EF BB BF:

Chr$(&HEF) & Chr$(&HBB) & Chr$(&HBF) = 

Po otwarciu pliku i wczytaniu do bufora pierwszej linii pliku za pomocą instrukcji Line Input # sprawdzamy, czy trzy pierwsze znaki określają znacznik BOM. Jeżeli tak, to usuwamy te znaki z bufora i zmieniamy wartość zmiennej fBOM na True, by nie sprawdzać w następnych liniach obecności znacznika BOM.

...
		' ustaw wartość znacznika BOM-Utf8
		sBOM_Utf8 = Chr$(&HEF) & Chr$(&HBB) & Chr$(&HBF)

	ff = FreeFile
	Open sFilePath For Input As #ff
		' czytaj po jednej linii do końca pliku
		Do While Not EOF(ff)
			Line Input #ff, sBuffer
			' usuń znacznik BOM z pierwszej linii
			If fBOM = False Then
				If InStr(1, sBuffer, sBOM_Utf8, vbBinaryCompare) > 0 Then
					sBuffer = Mid(sBuffer, 4)
				End If
				fBOM = True
			End If
...

Konwersja tekstu z formatu UTF8 na format ASCII.

Pobrane pliki *.csv kodowane są w UTF-8 i po bezpośrednim pobraniu zawartości pliku do tabel otrzymamy niezbyt czytelny tekst. Poniżej przykładowe trzy pozycje pliku WMRODZ.csv:

  • część    =   część
  • wieĹ›           =   wieś
  • przysiółek  =   przysiółek

Zagadnienie konwersji tekstu z formatu UTF-8 na format ASCII przedstawiłem na stronie: UTF8 »» ASCII.. Zaprezentowana tam funkcja tekstUTF8ToAscii(sUTF8 As String) As String konwertuje ciąg znaków z formatu UTF-8 na format Unicode (domyślną stroną kodową ASCII).

Option Compare Database
Option Explicit

#If VBA7 Then
	Private Declare PtrSafe Function MultiByteToWideChar Lib "kernel32" ( _
					ByVal CodePage As Long, _
					ByVal dwFlags As Long, _
					ByVal lpMultiByteStr As String, _
					ByVal cchMultiByte As Long, _
					ByVal lpWideCharStr As LongPtr, _
					ByVal cchWideChar As Long) As Long
	#Else
	Private Declare Function MultiByteToWideChar Lib "kernel32.dll" ( _
					ByVal CodePage As Long, _
					ByVal dwFlags As Long, _
					ByVal lpMultiByteStr As String, _
					ByVal cchMultiByte As Long, _
					ByVal lpWideCharStr As Long, _
					ByVal cchWideChar As Long) As Long
	#End If
	Private Const CP_UTF8 As Long = 65001

	Public Function tekstUTF8ToAscii(sUTF8 As String) As String
	Dim lLenAscii   As Long
	Const MB_ERR_INVALID_CHARS = 8
	Const ERROR_INVALID_FLAGS = 1004

	' pobierz wielkość potrzebnego buforu na wyjściowy ciąg znaków ASCII
	lLenAscii = MultiByteToWideChar(CP_UTF8, 0&, sUTF8, Len(sUTF8), 0&, 0&)
	' przygotuj bufor na przyjęcie ciągu po konwersji na Ascii
	tekstUTF8ToAscii = String$(lLenAscii, vbNullChar)
	' konwertuj wejściowy ciąg na ASCII
	lLenAscii = MultiByteToWideChar(CP_UTF8, 0&, sUTF8, Len(sUTF8), _
																	StrPtr(tekstUTF8ToAscii), lLenAscii)

End Function

Plik CSV

CSV (ang. Comma-S/eparated Values, wartości rozdzielone przecinkiem) – format przechowywania danych w plikach tekstowych. Poszczególne pola są oddzielone od siebie przecinkami (,), a każde z pól zajmuje tylko tyle miejsca, by pomieścić dane. Istnieje wiele implementacji standardu pliku w formacie CSV – wiele z nich nie trzyma się ściśle wszystkich określonych poniżej zasad.

• Końce linii
Poszczególne rekordy rozdzielone są znakami końca linii vbNewLine (vbCrLf).
Ostatnia linia w pliku może nie zawierać znaku końca.
Znak vbNewLine (vbCrLf) może być elementem pola, które musi wtedy być ujęte w cudzysłowy.
• Separator
Wartości pól zgodnie z nazwą formatu rozdzielone są przecinkami.
Jako separator pól bywa także stosowany znak średnika ';' (lub inny zgodnie z ustawieniami regionalnymi systemu) albo tabulator, jednak jest to niezalecane. W jednym pliku może być użyty tylko jeden rodzaj separatora.
• Cudzysłów
Wartości pól mogą być ujęte w cudzysłowy.
Wartości zawierające używany znak separatora (przecinek, średnik, znak tabulacji lub znaki końca linii) muszą być ujęte w cudzysłów.
Aby w treści pola umieścić cudzysłów należy wpisać znak cudzysłowu dwukrotnie, całą wartość ujmując w cudzysłów.
• Uwagi
Spacje i inne białe znaki (w szczególności te przyległe do separatorów) należą do pól.
Pierwsza linia może stanowić nagłówek zawierający nazwy pól rekordów, jednak pierwszy wiersz pliku CSV w/g standardu ma takie samo znaczenie jak pozostałe.

Filtr konwersji CSV z pakietu Microsoft Office pracuje przy założeniu, że plik CSV używa przecinka jako separatora, tymczasem Microsoft Excel i Access wyświetlają i zapisują plik CSV w formacie zgodnym z ustawieniami regionalnymi systemu, czyli w przypadku języka polskiego używa średnika, zamiast przecinka do rozdzielania pól. Aby umożliwić automatyczną konwersję, tworzone są dedykowane makra.

W oparciu o artykuł: Wikipedia. Plik csv

Czy pierwszy wiersz zawiera nazwy pól?

We wszystkich pełnych plikach Rejestru TERYT pierwszy wiersz zawiera trzyznakowy znacznik BOM (zazwyczaj niewidoczny w większości edytorów). Za znacznikiem BOM znajdują się rozdzielone średnikami ';' nazwy poszczególnych pól. Poniżej przedstawiam dwie pierwsze linijki pliku WMRODZ.csv:
 RM;NAZWA_RM;STAN_NA          00     część;     2013-02-28

Przy przetwarzaniu plików pełnych Rejestry TERYT mamy wcześniej przygotowane tabele, więc w trakcie wczytywania danych musimy pominąć pierwszą linię każdego pliku *.csv

Public Function fCsvWMRodzToTable(ByVal sFilePath As String, _
                         Optional ByVal sDelim As String = ";", _
                         Optional ByVal fFirstRowHasFieldNames As Boolean = True) As Boolean
...
	ff = FreeFile
	Open sFilePath For Input As #ff
		' czytaj po jednej linii do końca pliku
		Do While Not EOF(ff)
			Line Input #ff, sBuffer
			' nie uwzględniaj pustych wierszy pani Marysi (a takie się zdarzały)
			If Len(sBuffer) > 0 Then
				' pierwszy pełny wiersz zawiera nazwy pól
				If fFirstRowHasFieldNames = True Then
					' następne linie zawieraja już dane
					fFirstRowHasFieldNames = False
				Else
					' konwertuj tekst z Utf8 na ASCII
					sBuffer = tekstUTF8ToAscii(sBuffer)
					' rozdziel tekst względem separatora
					sFields() = Split(sBuffer, sDelim, , vbBinaryCompare)
...

Przeglądanie danych Rejestry TERYT.

W przykładowej bazie zamieściłem dodatkowe dwa formularze służące do przeglądania danych. Pierwszy z nich zawiera formant ComboBox za pomocą którego można wyświetlić dane w hierarchicznej strukturze drzewa TreeView. Więcej o formancie kombi w widoku TreeView znajdziesz na stronie Kombi w widoku TreeView


TERYT. Pole kombi w widoku TrreeView
Rejestr TERYT. Pole kombi w widoku TrreeView

Drugi z formularzy zawiera pięć hierarchicznych (kaskadowych) pól kombi Wybór pozycji w pierwszym polu kombi, ogranicza możliwość wyboru w drugim polu kombi tylko do pozycji powiązanych z wybraną wcześniej pozycją. Po wyborze elementu w drugim polu kombi, lista pozycji w trzecim polu kombi zostaje ograniczona do pozycji należących do wybranego wcześniej elementu drugiego pola kombi itd. Więcej o powiązanych hierarchicznie polach kombi znajdziesz na stronie Hierarchicznie pola kombi


TERYT. Hierarchiczne pola kombi
Rejestr TERYT. Hierarchiczne pola kombi