Πόσο μακριά βρίσκεται το κοντινότερο σχολείο; Xρησιμοποιήσαμε τα Γυμνάσια και τα Λύκεια της ελληνικής περιφέρειας ως παράδειγμα, για να χαρτογραφήσουμε τις αποστάσεις από μια βασική υπηρεσία και να δείξουμε πώς οι ισοαποστάσεις μπορούν να αποτυπωθούν σε χάρτη.
Μέρος του φαύλου κύκλου στην αντιμετώπιση του δημογραφικού είναι η σχέση μεταξύ της μείωσης του πληθυσμού σε επαρχιακές περιοχές και της ταυτόχρονης απομάκρυνσης υπηρεσιών από αυτές, καθώς οι λιγότεροι κάτοικοι δεν μπορούν να δικαιολογήσουν πολλά καταστήματα και υπηρεσίες. Έτσι, το πρόβλημα της απόστασης έρχεται συχνά στο προσκήνιο (π.χ. κλείσιμο των υποκαταστημάτων ΕΛΤΑ, κλείσιμο σχολικών μονάδων, διάθεση υπηρεσιών υγείας), καθώς ένας πολίτης σε κάποιον απομακρυσμένο οικισμό συχνά δεν μπορεί να έχει εύκολη πρόσβαση σε βασικές υπηρεσίες.
Για την περαιτέρω μελέτη αυτού του –όχι μόνο ελληνικού– προβλήματος, δημιουργήσαμε το παρόν χρηστικό άρθρο για τον υπολογισμό των ισοαποστάσεων (isodistances), δηλαδή τις «ζώνες» χιλιομετρικής απόστασης από υπηρεσίες. Εν προκειμένω, χρησιμοποιήσαμε ως παράδειγμα τις σχολικές μονάδες Γυμνασίων και Λυκείων στην Ελλάδα και δημιουργήσαμε έναν χάρτη, χωρίς σε αυτόν να συμπεριλαμβάνουμε τις περιοχές της Αττικής και της Θεσσαλονίκης.
Κώδικας και ένα δωρεάν εργαλείο
Για την εξαγωγή των ισοαποστάσεων χρησιμοποιήσαμε το API της δωρεάν υπηρεσίας openrouteservice, που έχει δημιουργηθεί από το Heidelberg Institute for Geoinformation Technology (HeiGIT) το οποίο, με τη σειρά του, χρησιμοποιεί τα γεωχωρικά δεδομένα του OpenStreetMap για τον υπολογισμό των ισοαποστάσεων. Τα δεδομένα των σχολικών μονάδων προέρχονται από το Πανελλήνιο Σχολικό Δίκτυο (ΠΣΔ), το εθνικό δίκτυο και πάροχο υπηρεσιών διαδικτύου του Υπουργείου Παιδείας και Θρησκευμάτων, που διασυνδέει και υποστηρίζει περισσότερες από 16.000 σχολικές μονάδες.
Κατεβάσαμε τα δεδομένα των ενεργών σχολικών μονάδων από την ιστοσελίδα του ΠΣΔ, ανά περιοχή, σε μορφή .xlsx, και τα ενοποιήσαμε σε ένα ενιαίο σύνολο δεδομένων (διαθέσιμο εδώ). Το dataset περιλαμβάνει στοιχεία για 15.097 ενεργές σχολικές και διοικητικές μονάδες σε όλη τη χώρα. Εκτός της ανάλυσης και της οπτικοποίησης έμειναν η Αττική και η Θεσσαλονίκη, λόγω του μεγάλου αριθμού σχολικών μονάδων και τη σχετικά μικρή απόσταση από αυτές. Ως εκ τούτου, ο χάρτης που παρουσιάζουμε παρακάτω περιλαμβάνει 2.266 σχολικές μονάδες.
Ανάκτηση και επεξεργασία γεωχωρικών δεδομένων από το OpenStreetMap

Τα δεδομένα του OpenStreetMap αποτελούν έναν «θησαυρό» σε κοινή θέα. Παρουσιάζουμε πώς κανείς μπορεί να ανακτήσει και να επεξεργαστεί στοιχεία του OpenStreetMap, με τη χρήση Python ή QGIS.
Η ζώνη ίσης απόστασης (isodistance/ ισοαποστάσεων) αποτελεί μια περιοχή που εκτείνεται από ένα σημείο, εντός της οποίας η απόσταση από αυτό δεν ξεπερνά ένα συγκεκριμένο μέγεθος. Παραδείγματος χάριν, στον παρακάτω χάρτη με μπλε σκούρο αποτυπώνεται η περιοχή εντός της οποίας η εμβληματική Παναγία των Παρισίων (η κόκκινη «πινέζα») βρίσκεται λιγότερο από ένα χιλιόμετρο μακριά, ενώ με ελαφρό γαλαζοπράσινο χρώμα η ζώνη εντός της οποίας είναι λιγότερο από δύο χιλιόμετρα μακριά. Ο υπολογισμός αυτός δεν γίνεται σαν να είναι η ακτίνα ενός κύκλου, σε ευθεία γραμμή, αλλά λαμβάνοντας υπόψη το οδικό δίκτυο της περιοχής, για να εντοπιστεί πώς ένας πεζός ή ένα όχημα θα φτάσει στο σημείο.

Λόγω του περιορισμού που εφαρμόζει το openrouteservice στην επεξεργασία ισοαποστάσεων (500 ανά ημέρα), επιλέξαμε να προχωρήσουμε με τον υπολογισμό των ισοαποστάσεων μόνο για τα Γυμνάσια και τα Λύκεια. Καθώς ο αριθμός των σχολικών μονάδων είναι και πάλι πάνω από το όριο (περισσότερες από 2.000) ο κώδικας που έχουμε συντάξει περιέχει την πρόβλεψη ο υπολογισμός να γίνει σε επιμέρους τμήματα.
Κώδικας για υπολογισμό ζωνών απόστασης από κάθε σχολική μονάδα
# ── 0. Install
!pip install -q geopandas folium shapely openrouteservice pyogrio
# ── 1. Imports
import time
import pickle
from pathlib import Path
import pandas as pd
import geopandas as gpd
import folium
import openrouteservice as ors
from shapely.geometry import shape
from openrouteservice.exceptions import ApiError
# ── 2. Configuration
RADII_KM = [5, 10, 25, 50]
COLORS = {
5: "#2166ac",
10: "#74add1",
25: "#fdae61",
50: "#d73027",
}
CHECKPOINT = Path("/content/isodistances_raw.pkl")
ERRORS_CSV = "/content/isodistances_errors.csv"
OUTPUT_GPKG = "/content/isodistances.gpkg"
OUTPUT_HTML = "/content/isodistances_road.html"
# Safe rate for the standard isochrone limit
SLEEP_SECONDS = 3.2
MAX_RETRIES = 3
# ── 3. Prepare locations
nip = df_test.copy()
nip["Γεωγραφικό Πλάτος"] = pd.to_numeric(
nip["Γεωγραφικό Πλάτος"],
errors="coerce",
)
nip["Γεωγραφικό Μήκος"] = pd.to_numeric(
nip["Γεωγραφικό Μήκος"],
errors="coerce",
)
nip = nip.dropna(
subset=[
"Κωδικός ΜΜ",
"Ονομασία",
"Γεωγραφικό Πλάτος",
"Γεωγραφικό Μήκος",
]
).copy()
nip = nip[
nip["Γεωγραφικό Πλάτος"].between(-90, 90)
& nip["Γεωγραφικό Μήκος"].between(-180, 180)
].copy()
origins_gdf = gpd.GeoDataFrame(
nip,
geometry=gpd.points_from_xy(
nip["Γεωγραφικό Μήκος"],
nip["Γεωγραφικό Πλάτος"],
),
crs="EPSG:4326",
)
print(f"✓ Valid locations for calculation: {len(origins_gdf)}")
# ── 4. Create ORS client
client = ors.Client(
key=ORS_KEY.strip(),
base_url="https://api.heigit.org/openrouteservice",
)
# ── 5. Load existing checkpoint, if available
if CHECKPOINT.exists():
with CHECKPOINT.open("rb") as f:
results = pickle.load(f)
print(f"✓ Loaded checkpoint with {len(results)} completed origins")
else:
results = {}
errors = []
# ── 6. Calculate isodistances for all df_test locations
for position, (_, row) in enumerate(origins_gdf.iterrows(), start=1):
mm_id = row["Κωδικός ΜΜ"]
# Skip locations already successfully obtained in a prior run.
if mm_id in results:
continue
lat = float(row["Γεωγραφικό Πλάτος"])
lon = float(row["Γεωγραφικό Μήκος"])
try:
for attempt in range(1, MAX_RETRIES + 1):
try:
response = client.isochrones(
locations=[[lon, lat]], # ORS coordinate order: [lon, lat]
profile="driving-car",
range_type="distance",
range=[radius * 1000 for radius in RADII_KM],
)
break
except ApiError as e:
error_text = str(e)
# Retry only rate limiting, with increasing pauses.
if "429" in error_text and attempt < MAX_RETRIES:
wait_seconds = 30 * attempt
print(
f"Rate limit at {position}/{len(origins_gdf)}. "
f"Waiting {wait_seconds}s; retry {attempt + 1}/{MAX_RETRIES}."
)
time.sleep(wait_seconds)
continue
raise
results[mm_id] = {
"name": row["Ονομασία"],
"lat": lat,
"lon": lon,
"features": response["features"],
}
print(
f"✓ {position}/{len(origins_gdf)} "
f"| {mm_id} | total cached: {len(results)}"
)
except Exception as e:
errors.append({
"position": position,
"Κωδικός ΜΜ": mm_id,
"Ονομασία": row["Ονομασία"],
"lat": lat,
"lon": lon,
"error": str(e),
})
print(f"✗ {position}/{len(origins_gdf)} | {mm_id}: {e}")
# A 403 is an access/key/host-side problem: persist results and stop.
# Re-run later; cached locations will be skipped.
if "403" in str(e):
print("Stopping after 403; checkpoint has been saved.")
break
finally:
with CHECKPOINT.open("wb") as f:
pickle.dump(results, f)
pd.DataFrame(errors).to_csv(ERRORS_CSV, index=False)
time.sleep(SLEEP_SECONDS)
print(f"\nFinished or paused.")
print(f"Completed origins: {len(results)}")
print(f"Errors recorded: {len(errors)}")Ένα βασικό πρόβλημα που αντιμετωπίσαμε προσπαθώντας να υπολογίσουμε τις ισοαποστάσεις με το openrouteservice ήταν οι θαλάσσιες συνδέσεις. Η υπηρεσία του ινστιτούτου HeiGIT εντάσσει στον υπολογισμό της και τις διαδρομές των πλοίων σαν να ήταν μια οδική σύνδεση. Αυτό προφανώς έπρεπε να διορθωθεί. Έτσι, σε δεύτερο στάδιο και ενώ είχαμε ήδη υπολογισμένες τις ισοαποστάσεις των σχολικών μονάδων των νησιών, περιορίσαμε τα πολύγωνα των χιλιομετρικών ζωνών στα όρια της ακτογραμμής τους, με την εξαίρεση των δύο ελληνικών νησιών που διαθέτουν χερσαία σύνδεση, την Εύβοια και τη Λευκάδα.
Κώδικας για την ενοποίηση των ζωνών και τη χαρτογράφησή τους σε διαδραστικό χάρτη
# ─ 0. Imports
import pickle
from pathlib import Path
from collections import defaultdict
import geopandas as gpd
import folium
from shapely.geometry import Point, box, shape, mapping
from shapely.ops import unary_union
from folium.plugins import MarkerCluster
# ── 1. Configuration
CHECKPOINT = Path("/content/isodistances_raw.pkl")
COLORS = {
5: "#2166ac",
10: "#74add1",
25: "#fdae61",
50: "#d73027",
}
OUTPUT_MAP = "/content/isodistances_strict_coastline.html"
# ── 2. Load isodistance results
with CHECKPOINT.open("rb") as f:
results = pickle.load(f)
if not results:
raise ValueError("Checkpoint contains no ORS results.")
print(f"✓ Loaded {len(results)} origins from checkpoint")
# ── 3. Load high-resolution Natural Earth land polygons
print("Loading high-resolution land polygons...")
world_land = gpd.read_file(
"https://naciscdn.org/naturalearth/10m/physical/ne_10m_land.zip"
).to_crs("EPSG:4326")
greece_bbox = box(19.0, 34.0, 30.0, 42.0)
land_gr = gpd.overlay(
world_land,
gpd.GeoDataFrame(geometry=[greece_bbox], crs="EPSG:4326"),
how="intersection",
)
land_gr = (
land_gr
.explode(index_parts=False)
.reset_index(drop=True)
)
# Fix invalid topology without expanding shorelines.
land_gr["geometry"] = land_gr.geometry.buffer(0)
if land_gr.empty:
raise ValueError("No Greek land polygons were loaded.")
print(f"✓ Separate landmass polygons: {len(land_gr)}")
# ── 4. Identify mainland, Lefkada and Euboea land polygons
MAINLAND_POINT = Point(21.20, 39.40)
LEFKADA_POINT = Point(20.70, 38.71)
EUBOEA_POINT = Point(23.88, 38.60)
sindex = land_gr.sindex
def find_landmass_index(point):
"""Return the exact land polygon containing a point, or nearest fallback."""
matches = list(sindex.query(point, predicate="intersects"))
if matches:
return matches[0]
return land_gr.geometry.distance(point).idxmin()
mainland_idx = find_landmass_index(MAINLAND_POINT)
lefkada_idx = find_landmass_index(LEFKADA_POINT)
euboea_idx = find_landmass_index(EUBOEA_POINT)
mainland_geom = land_gr.geometry.iloc[mainland_idx]
lefkada_geom = land_gr.geometry.iloc[lefkada_idx]
euboea_geom = land_gr.geometry.iloc[euboea_idx]
print("✓ Mainland, Lefkada, and Euboea polygons identified")
# ── 5. Create Euboea mainland exception
CHALKIDA_BOX = box(
23.566754159443928,
38.434796490238064,
23.642937295595654,
38.48266986633943,
)
METRIC_CRS = "EPSG:3857"
CUT_BUFFER_M = 2_000
euboea_m = (
gpd.GeoSeries([euboea_geom], crs="EPSG:4326")
.to_crs(METRIC_CRS)
.iloc[0]
)
mainland_m = (
gpd.GeoSeries([mainland_geom], crs="EPSG:4326")
.to_crs(METRIC_CRS)
.iloc[0]
)
chalkida_m = (
gpd.GeoSeries([CHALKIDA_BOX], crs="EPSG:4326")
.to_crs(METRIC_CRS)
.iloc[0]
)
potential_contacts_m = (
euboea_m.buffer(CUT_BUFFER_M)
.intersection(mainland_m.buffer(CUT_BUFFER_M))
)
cut_outside_chalkida_m = potential_contacts_m.difference(
chalkida_m.buffer(1_000)
)
cut_outside_chalkida = (
gpd.GeoSeries([cut_outside_chalkida_m], crs=METRIC_CRS)
.to_crs("EPSG:4326")
.iloc[0]
)
# ── 5b. Lefkada exception
LEFKADA_NORTH_BOX = box(20.669099, 38.795963, 20.768686, 38.866302)
# ── 6. Define allowed land geometry for each school
LEFKADA_BBOX = box(20.55, 38.60, 20.75, 38.85)
def permitted_land_for_origin(lat, lon):
"""
Default: retain only the origin's own land polygon.
Lefkada: allow Lefkada, mainland, and the bridge box that spans the causeway.
Euboea: allow Euboea and mainland only through Chalkida.
"""
origin_point = Point(lon, lat)
origin_idx = find_landmass_index(origin_point)
origin_land = land_gr.geometry.iloc[origin_idx]
is_lefkada = (origin_idx == lefkada_idx) or LEFKADA_BBOX.contains(origin_point)
if is_lefkada:
allowed = unary_union([lefkada_geom, origin_land, mainland_geom, LEFKADA_NORTH_BOX])
return (
allowed,
"Lefkada → mainland via road causeway only",
origin_point,
)
if origin_idx == euboea_idx:
allowed = unary_union([euboea_geom, mainland_geom])
allowed = allowed.difference(cut_outside_chalkida)
return (
allowed,
"Euboea → mainland via Chalkida only",
origin_point,
)
return origin_land, "same landmass only", origin_point
def polygon_parts(geometry):
"""Extract Polygon parts, including from GeometryCollections."""
if geometry.is_empty:
return []
if geometry.geom_type == "Polygon":
return [geometry]
if geometry.geom_type == "MultiPolygon":
return list(geometry.geoms)
if geometry.geom_type == "GeometryCollection":
parts = []
for item in geometry.geoms:
if item.geom_type == "Polygon":
parts.append(item)
elif item.geom_type == "MultiPolygon":
parts.extend(item.geoms)
return parts
return []
def keep_component_connected_to_origin(geometry, origin_point):
"""
Keep the component covering the origin; otherwise retain nearest component.
This removes disconnected land/island areas from a polygon.
"""
parts = polygon_parts(geometry)
if not parts:
return None
covering_origin = [
part for part in parts
if part.covers(origin_point)
]
if covering_origin:
return max(covering_origin, key=lambda part: part.area)
return min(parts, key=lambda part: part.distance(origin_point))
# ── 7. Strict per-school/per-radius coastline clipping
fine_land = unary_union(land_gr.geometry.tolist() + [LEFKADA_NORTH_BOX]).buffer(0)
radius_polygons = defaultdict(list)
origin_modes = defaultdict(int)
for mm_id, data in results.items():
permitted_land, mode, origin_point = permitted_land_for_origin(
data["lat"],
data["lon"],
)
origin_modes[mode] += 1
# Land allowed for this particular school
school_allowed_land = permitted_land.intersection(fine_land)
if school_allowed_land.is_empty:
print(f"⚠ No valid land for {mm_id}: {data['name']}")
continue
for feature in data["features"]:
radius_km = int(feature["properties"]["value"] / 1000)
ors_polygon = shape(feature["geometry"])
# Strictly constrain every ORS polygon to actual land
coastline_clipped = ors_polygon.intersection(school_allowed_land)
# Retain only the land component connected to the origin
connected_component = keep_component_connected_to_origin(
coastline_clipped,
origin_point,
)
if (
connected_component is not None
and not connected_component.is_empty
):
radius_polygons[radius_km].append(connected_component)
print("\n✓ Origin handling:")
for mode, count in origin_modes.items():
print(f" {count}: {mode}")
print("\n✓ Retained individual polygons:")
for radius in sorted(radius_polygons):
print(f" {radius} km: {len(radius_polygons[radius])}")
# ── 8. Merge polygons by radius and make non-overlapping bands
union_by_radius = {
radius: unary_union(polygons)
for radius, polygons in radius_polygons.items()
if polygons
}
sorted_radii = sorted(union_by_radius)
if not sorted_radii:
raise ValueError("No valid isodistance polygons were retained.")
donuts = {}
previous_union = None
for radius in sorted_radii:
current_union = union_by_radius[radius]
if previous_union is None:
donuts[radius] = current_union
previous_union = current_union
else:
donuts[radius] = current_union.difference(previous_union)
previous_union = unary_union([previous_union, current_union])
print(f"\n✓ Final bands: {sorted_radii}")
# ── 9. Create interactive map
origin_lats = [data["lat"] for data in results.values()]
origin_lons = [data["lon"] for data in results.values()]
m = folium.Map(
location=[
sum(origin_lats) / len(origin_lats),
sum(origin_lons) / len(origin_lons),
],
zoom_start=7,
tiles="CartoDB positron",
)
for radius in sorted(donuts.keys(), reverse=True):
geometry = donuts[radius]
if geometry.is_empty:
continue
smaller_radii = [r for r in sorted_radii if r < radius]
lower_radius = max(smaller_radii) if smaller_radii else 0
label = (
f"0–{radius} km"
if lower_radius == 0
else f"{lower_radius}–{radius} km"
)
folium.GeoJson(
mapping(geometry),
name=label,
style_function=lambda _, color=COLORS.get(radius, "#aaaaaa"): {
"fillColor": color,
"color": color,
"weight": 1,
"fillOpacity": 0.35,
},
tooltip=f"{label} road isodistance",
).add_to(m)
marker_cluster = MarkerCluster(name="Σχολεία").add_to(m)
for mm_id, data in results.items():
folium.Marker(
location=[data["lat"], data["lon"]],
tooltip=f"{data['name']} (Κωδικός ΜΜ: {mm_id})",
).add_to(marker_cluster)
m.fit_bounds([
[min(origin_lats), min(origin_lons)],
[max(origin_lats), max(origin_lons)],
])
folium.LayerControl(collapsed=False).add_to(m)
m.save(OUTPUT_MAP)
print(f"✓ Map saved: {OUTPUT_MAP}")
m
Στον χάρτη που δημιουργήσαμε εντοπίζονται όλα τα Γυμνάσια και Λύκεια (συμπεριλαμβανομένων των Γυμνασίων με Λυκειακές Τάξεις, τα Ενιαία Επαγγελματικά Γυμνάσια καθώς και τα Επαγγελματικά Λύκεια) ως μωβ σημεία εντός της επικράτειας, από τα οποία «εξαπλώνονται» τέσσερις διαφορετικές χρωματικές ζώνες, που απεικονίζουν τη χιλιομετρική απόσταση από αυτά (έως 5 χλμ., 5-10 χλμ., 10-25 χλμ., άνω των 25 χλμ.), οπτικοποιώντας έτσι περιοχές οι μαθητές των οποίων πρέπει να μετακινηθούν σημαντικά για να έχουν πρόσβαση στα σχολεία. Ταυτόχρονα, με γκρίζο χρώμα εντοπίζονται νησιά (εκτός περιφέρειας Αττικής) που δεν διαθέτουν δευτεροβάθμιες σχολικές μονάδες. Σημειώνεται ότι η χαρτογράφηση είναι κατά προσέγγιση, καθώς οι ζώνες που παρέχει το openrouteservice φαίνεται να έχουν κάποιες αποκλίσεις.
Xάρτης ισοαποστάσεων από Γυμνάσια και Λύκεια ανά την Ελλάδα
Όπως φαίνεται και στον παραπάνω χάρτη, υπάρχουν αρκετοί οικισμοί της επικράτειας όπου οικισμοί, στους οποίους πιθανώς κατοικούν μαθητές, βρίσκονται δεκάδες χιλιόμετρα από τις σχολικές μονάδες, αναγκάζοντας τους έτσι να κάνουν συνολικές διαδρομές άνω των 50 χιλιομέτρων καθημερινά, για να έχουν πρόσβαση στην εκπαίδευση. Σε αυτήν την κατηγορία αποστάσεων, βρίσκονται, όπως προκύπτει από την ανάλυση των γεωχωρικών δεδομένων, οικισμοί του νομού Ευρυτανίας, του μικρότερου πληθυσμιακά νομού της ηπειρωτικής χώρας, αλλά και γενικότερα ορεινές περιοχές σε όλο το μήκος της οροσειράς της Πίνδου.
Αυτό που δεν φαίνεται στον χάρτη, όμως, είναι πόσοι μαθητές μπορεί να υπάρχουν σε αυτούς τους οικισμούς. Ωστόσο, η συγκεκριμένη προσέγγιση είναι χρήσιμη για να χαρτογραφήσουμε περιοχές που οι κρατικές (ή άλλες) υπηρεσίες είναι απούσες, είτε πρόκειται για σχολεία, είτε για νοσοκομεία, φαρμακεία, αστυνομικά τμήματα, κλπ.
Πατώντας (στο κινητό) ή κάνοντας κλικ σε οποιοδήποτε σημείο στον χάρτη, μπορείτε να δείτε ποιο σχολείο είναι το κοντινότερο και ποια είναι η απόσταση έως αυτό. Ο υπολογισμός της απόστασης γίνεται δυναμικά, μέσω του OSRM Route API, μια πλατφόρμα ανοιχτού κώδικα με την οποία μπορούν να εντοπίζονται διαδρομές μεταξύ διαφορετικών σημείων. Σε ορισμένα σημεία του χάρτη, το OSRM Route API ακολουθεί διαφορετικό οδικό δίκτυο από αυτό που χρησιμοποίησε το openrouteservice για τον υπολογισμό της ζώνης απόστασης, οδηγώντας σε αποκλίσεις.
Οδηγός ανάλυσης χαρτογραφικών δεδομένων με το QGIS

Ένας αναλυτικός οδηγός για το QGIS: από την εγκατάσταση του συστήματος και την φόρτωση δεδομένων έως την ανάλυση των στοιχείων.
Προβλήματα γεωγραφίας
Από τις σημαντικότερες δυσκολίες κατά τη διάρκεια ανάπτυξης αυτού του χάρτη ήταν η γεωμορφολογία της χώρας (βουνά, λίμνες, κλπ.), όπου πολλές περιοχές δεν διαθέτουν κατάλληλο οδικό δίκτυο. Καθώς το openrouteservice χρησιμοποιεί το οδικό δίκτυο για να υπολογίζει τις ζώνες, σε σημεία που δεν υπήρχαν οδοί ανέβαζε την κατηγορία απόστασης ή δεν συμπεριλάμβανε την περιοχή αυτή καθόλου. Παραδείγματα τέτοια εντοπίστηκαν από τον Όλυμπο και ορεινούς όγκους νησιών έως παραλιακές περιοχές σε νησιά και την ηπειρωτική χώρα. Η οπτικοποίηση, έτσι, αρχικά έφτασε να είναι υπερβολικά κατακερματισμένη και «θορυβώδης».

Έτσι, μεταφέραμε τα δεδομένα των ζωνών ίσων αποστάσεων στο QGIS, όπου και εντοπίσαμε περιοχές που δεν καλύπτονται από το βασικό οδικό δίκτυο της χώρας, και τις τροποποιήσαμε σε κατηγορία «άνευ δρόμων». Η κατηγοριοποίηση ως «βασικό οδικό δίκτυο» έγινε μέσω των δεδομένων του OSM, ενοποιώντας το οδικό δίκτυο της ηπειρωτικής Ελλάδας και των νησιών σε ένα ενιαίο και διαγράφοντας διάσπαρτα μικρά οδικά δίκτυα σε ορεινές περιοχές, για να εντοπιστούν ζώνες όπου στην πραγματικότητα δεν είναι δυνατή η πρόσβαση με όχημα. Έπειτα τις ζώνες αυτές τις διασπάσαμε σύμφωνα με τις κοντινότερες γειτονικές ζώνες που διέθεταν σύνδεση με το οδικό δίκτυο και έλαβαν την κατηγορία αυτών.

Ισοαποστάσεις χωρίς κώδικα
Η προσέγγιση που ακολουθήθηκε στο παρόν άρθρο (openrouteservice, QGIS, κλπ.) δεν είναι απαραίτητη για κάθε υπολογισμό ζωνών ίσης απόστασης. Υπάρχουν αρκετά άλλα εργαλεία online, ακόμα και το ίδιο το openrouteservice, με τα οποία μπορείτε να οπτικοποιήσετε την απόσταση από ένα σημείο άμεσα και χωρίς να εμπλακείτε με κώδικα ή λογισμικά επεξεργασίας γεωχωρικών δεδομένων. Ιστοσελίδες όπως το iso4app, το distance.to, ή το calcmaps μπορούν να κάνουν μια γρήγορη χαρτογράφηση για την εγγύτητα που έχει ένα νοσοκομείο, ένα σχολείο ή ένα οποιοδήποτε σημείο του χάρτη.
Τα γεωχωρικά αρχεία, το σύνολο δεδομένων με όλες τις σχολικές μονάδες ανά την Ελλάδα και ενδεικτικό μέρος του κώδικα που χρησιμοποιήθηκαν για το συγκεκριμένο χρηστικό άρθρο είναι ανοιχτά διαθέσιμα σε σχετικό αποθετήριο στο GitHub.
