Sağlık hizmeti sağlayıcı bulucu, bir geliştiricinin yapabileceği en sorumluluk gerektiren haritalardan biridir. Bu haritayı kullanan hastalar çoğu zaman endişelidir, bir yerleri ağrıyordur ya da tanımadıkları bir sistemde yollarını bulmaya çalışıyordur. Yavaş yüklenen, alakasız sonuçlar döndüren ya da posta kodlarını fark ettirmeden ABD merkezli üçüncü taraf bir hizmete gönderen bir harita yalnızca kötü bir kullanıcı deneyimi değildir, olası bir GDPR ihlalidir.
Bu eğitimde canlı ortama hazır bir sağlayıcı bulucu geliştiriyoruz: uzmanlık filtresi, Geocoding API ile posta kodu araması, "20 dakika içindeki doktorlar" katmanını gösteren bir izokron ve MapAtlas Maps API ile çalışan bir harita. GDPR uyumluluğu sonradan eklenmiyor, en baştan mimarinin parçası olarak kuruluyor.
Uygulamanın tamamı vanilla JavaScript ile yazıldı, herhangi bir framework gerekmiyor. React, Vue, düz HTML, WordPress ya da bir hastanenin eski CMS'i dahil her altyapıda çalışır. Eğitim ayrıca AB düzenleyici çerçevesini de ele alıyor: Birleşik Krallık'taki CQC verileri, Almanya'daki Kassenärztliche Vereinigung ve Fransa'daki CNAM. Böylece sağlayıcı verilerini yasal olarak nereden alacağınızı bilirsiniz.
Sağlık konum uygulamalarının neden ek GDPR yükümlülükleri var
Standart uygulamalar kişisel veri (ad, e-posta, kullanım bilgisi) toplar. Sağlık uygulamalarında ise ikinci bir risk vardır: konum verisinden sağlık bilgisi çıkarılması.
Bir kullanıcı "SE1 7PB yakınındaki kardiyologlar" diye arama yaptığında posta kodu kişisel veridir. Posta kodu ile tıbbi uzmanlık alanının bir araya gelmesi, GDPR Madde 9(1) kapsamında özel nitelikli veri, yani sağlıkla ilgili veri sayılabilir, çünkü Avrupa Birliği Adalet Divanı hassas bilgileri dolaylı olarak ortaya koyan verilerin de Madde 9 kapsamına girdiğine hükmetmiştir. Hiç kullanıcı hesabı oluşturmasanız ya da profil saklamasanız bile bu geçerlidir.
Riskler somuttur:
- Kullanıcının yazdığı posta kodunu içeren istemci tarafı bir geocoding API çağrısı, üçüncü taraf bir sunucuya gider. Bu sunucu posta kodunu, aranan uzmanlık alanını ve IP adresini görür. DPA ve AB veri yerleşimi garantisi olmadan bu sorunlu bir veri aktarımıdır.
- Tarayıcının otomatik tamamlama ve geçmiş özellikleri, yazılan posta kodunu ve uzmanlık sorgusunu kullanıcının cihazında saklayabilir. Bu sizin kontrolünüz dışındadır, ancak gizlilik bilgilendirmenizde belirtmeye değer.
- API yanıtlarını sunucu tarafında kayda alıyorsanız (hata ayıklama için yaygın bir uygulama), farkında olmadan posta kodu ve uzmanlık alanı eşleşmelerini kaydediyor olabilirsiniz.
Çözüm bir backend proxy kullanmaktır: sunucunuz aramayı alır, MapAtlas Geocoding API'yi çağırır ve yalnızca koordinatları döndürür. Yazılan sorgu hastanın tarayıcısından hiçbir zaman üçüncü taraf bir API'ye gönderilmez. Ayrıntıları uygulama bölümünde bulabilirsiniz.
Sağlayıcı veri yapısı
Sağlayıcı verileri, kazınmış (scrape edilmiş) veri setlerinden değil, güvenilir resmî kaynaklardan gelmelidir:
- Birleşik Krallık: NHS England'ın Directory of Healthcare Services (Service Search) API'si ve CQC'ye kayıtlı sağlayıcı listeleri
- Almanya: Kassenärztliche Bundesvereinigung (KBV) açık verileri ve Arztsuche API
- Fransa: CNAM'ın Ameli API'si ve RPPS rehberi (répertoire RPPS)
Her sağlayıcıyı bir GeoJSON feature olarak yapılandırın:
const providers = {
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: { type: "Point", coordinates: [4.9051, 52.3680] },
properties: {
id: "prov-001",
name: "Dr. Maria van den Berg",
specialty: "general-practitioner",
accepting: true,
address: "Prinsengracht 263, 1016 GV Amsterdam",
phone: "+31 20 423 5678",
languages: ["Dutch", "English"],
nextAvailable: "2026-02-18"
}
},
{
type: "Feature",
geometry: { type: "Point", coordinates: [4.8890, 52.3610] },
properties: {
id: "prov-002",
name: "Dr. Jan Smits",
specialty: "cardiologist",
accepting: false,
address: "Leidseplein 15, 1017 PS Amsterdam",
phone: "+31 20 612 3456",
languages: ["Dutch"],
nextAvailable: null
}
},
{
type: "Feature",
geometry: { type: "Point", coordinates: [4.9200, 52.3750] },
properties: {
id: "prov-003",
name: "Anna Fischer, MD",
specialty: "dermatologist",
accepting: true,
address: "Plantage Middenlaan 14, 1018 DD Amsterdam",
phone: "+31 20 789 0123",
languages: ["Dutch", "English", "German"],
nextAvailable: "2026-02-20"
}
}
]
};
Adım 1: Haritayı uzmanlık filtresiyle başlatın
Haritayla ve bir uzmanlık açılır menüsüyle başlayın. Filtre, görünen işaretçileri herhangi bir ağ isteği yapmadan istemci tarafında günceller.
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';
const map = new mapmetricsgl.Map({
container: 'provider-map',
style: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/style.json&token=YOUR_API_KEY',
center: [4.9041, 52.3676],
zoom: 13
});
map.on('load', () => {
map.addSource('providers', {
type: 'geojson',
data: providers
});
// Sağlayıcı işaretçileri, yeşil = hasta kabul ediyor, gri = dolu
map.addLayer({
id: 'provider-markers',
type: 'circle',
source: 'providers',
paint: {
'circle-radius': 10,
'circle-color': [
'case',
['==', ['get', 'accepting'], true], '#16A34A',
'#9CA3AF'
],
'circle-stroke-width': 2,
'circle-stroke-color': '#ffffff'
}
});
buildList(providers.features);
});
// Uzmanlık filtresi
document.getElementById('specialty-select').addEventListener('change', (e) => {
const specialty = e.target.value;
if (specialty === 'all') {
map.setFilter('provider-markers', null);
} else {
map.setFilter('provider-markers', ['==', ['get', 'specialty'], specialty]);
}
});
Adım 2: Backend proxy ile posta kodu araması
Geocoding API'yi tarayıcıdan çağırmak hastanın posta kodunu üçüncü taraf bir sunucuya açar. Bunun yerine geocoding isteğini kendi backend endpoint'iniz üzerinden yönlendirin. Sunucunuz MapAtlas'ı çağırır ve yalnızca koordinatları döndürür.
Frontend (hastanın tarayıcısı):
async function geocodePostcode(postcode) {
// Geocoding API'yi doğrudan değil, KENDİ backend'inizi çağırın
const res = await fetch(`/api/geocode?q=${encodeURIComponent(postcode)}`);
const { lat, lng } = await res.json();
return { lat, lng };
}
document.getElementById('postcode-form').addEventListener('submit', async (e) => {
e.preventDefault();
const postcode = document.getElementById('postcode-input').value.trim();
const { lat, lng } = await geocodePostcode(postcode);
map.flyTo({ center: [lng, lat], zoom: 13 });
fetchIsochrone(lat, lng);
});
Backend proxy (Node.js/Express):
// /api/geocode, yalnızca sunucu tarafında, posta kodu altyapınızın dışına çıkmaz
app.get('/api/geocode', async (req, res) => {
const url = new URL('https://gateway.mapmetrics-atlas.net/forward-geocode/');
url.searchParams.set('text', req.query.q);
url.searchParams.set('token', process.env.MAPATLAS_API_KEY); // sunucu tarafı anahtar
url.searchParams.set('size', '1');
const upstream = await fetch(url.toString());
const data = await upstream.json();
if (!data.features?.length) {
return res.status(404).json({ error: 'Postcode not found' });
}
const [lng, lat] = data.features[0].geometry.coordinates;
// YALNIZCA koordinatları döndür, posta kodu kaydedilmez, yanıtta uzmanlık alanı yok
res.json({ lat, lng });
});
API anahtarınız sunucuda kalır, hastanın tarayıcısı geocoder ile hiçbir zaman doğrudan iletişim kurmaz ve sunucunuzun dışarıya ilettiği tek şey, uzmanlık alanı ya da hastayı tanımlayan herhangi bir bilgi olmadan yalnızca posta kodunun kendisidir.
Adım 3: İzokron katmanı, "20 dakika içindeki doktorlar"
İzokron poligonu, belirli bir sürüş süresi içinde ulaşılabilen tüm noktaları gösterir. Sağlayıcı bulucuda bir izokron göstermek, hastanın en pratik sorusunu yanıtlar: "Gerçekte kaç doktora ulaşabilirim?"
async function fetchIsochrone(lat, lng) {
// Aynı gizlilik nedenleriyle backend proxy üzerinden çağırın
const res = await fetch(
`/api/isochrone?lat=${lat}&lng=${lng}&minutes=20&costing=auto`
);
const geojson = await res.json();
// Varsa mevcut izokron katmanını kaldır
if (map.getLayer('isochrone-fill')) map.removeLayer('isochrone-fill');
if (map.getLayer('isochrone-border')) map.removeLayer('isochrone-border');
if (map.getSource('isochrone')) map.removeSource('isochrone');
map.addSource('isochrone', { type: 'geojson', data: geojson });
map.addLayer({
id: 'isochrone-fill',
type: 'fill',
source: 'isochrone',
paint: {
'fill-color': '#16A34A',
'fill-opacity': 0.12
}
}, 'provider-markers'); // İşaretçilerin altına ekle, pinler üstte görünsün
map.addLayer({
id: 'isochrone-border',
type: 'line',
source: 'isochrone',
paint: {
'line-color': '#16A34A',
'line-width': 2,
'line-dasharray': [3, 2]
}
}, 'provider-markers');
}
Backend izokron proxy'si:
app.get('/api/isochrone', async (req, res) => {
const { lat, lng, minutes, costing } = req.query;
const url = new URL('https://gateway.mapmetrics-atlas.net/isochrone/');
url.searchParams.set('token', process.env.MAPATLAS_API_KEY);
const upstream = await fetch(url.toString(), {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
locations: [{ lat: Number(lat), lon: Number(lng) }],
costing: costing || 'auto',
contours: [{ time: Number(minutes) }],
polygons: true,
}),
});
const data = await upstream.json();
res.json(data); // GeoJSON poligonu
});
İzokron haritaları yazısı izokron kullanım senaryolarını daha ayrıntılı ele alıyor. Buna toplu taşıma izokronları (arabası olmayan hastalar için faydalı) ve 10, 20 ve 30 dakikalık bölgeleri aynı anda gösteren çok katmanlı süre bantları da dahil.
Adım 4: Sağlayıcı ayrıntı açılır penceresi
Hasta bir doktor işaretçisine tıkladığında temel bilgileri gösterin: ad, uzmanlık alanı, hasta kabul durumu, konuşulan diller ve en yakın uygun randevu.
map.on('click', 'provider-markers', (e) => {
const {
name, specialty, accepting, address, phone, languages, nextAvailable
} = e.features[0].properties;
const coords = e.features[0].geometry.coordinates.slice();
const statusBadge = accepting
? '<span style="color:#16A34A;font-weight:600">Accepting patients</span>'
: '<span style="color:#9CA3AF">Not accepting new patients</span>';
const apptLine = accepting && nextAvailable
? `<p style="margin:4px 0">Next available: <strong>${nextAvailable}</strong></p>`
: '';
new mapmetricsgl.Popup({ maxWidth: '280px' })
.setLngLat(coords)
.setHTML(`
<strong style="font-size:15px">${name}</strong>
<p style="margin:4px 0;text-transform:capitalize">${specialty.replace('-', ' ')}</p>
${statusBadge}
${apptLine}
<p style="margin:6px 0;font-size:13px;color:#64748b">${address}</p>
<p style="margin:4px 0;font-size:13px">
Languages: ${Array.isArray(languages) ? languages.join(', ') : languages}
</p>
<a href="tel:${phone}" style="color:#2563EB">${phone}</a>
`)
.addTo(map);
});
map.on('mouseenter', 'provider-markers', () => { map.getCanvas().style.cursor = 'pointer'; });
map.on('mouseleave', 'provider-markers', () => { map.getCanvas().style.cursor = ''; });
Adım 5: Haritayla senkronize liste paneli
Haritanın yanındaki bir liste görünümü, ekran okuyucu kullananlara ve pinlere tıklamak yerine kaydırmayı tercih edenlere yardımcı olur.
function buildList(features) {
const list = document.getElementById('provider-list');
list.innerHTML = '';
features.forEach(({ properties, geometry }) => {
const { name, specialty, accepting, address, nextAvailable } = properties;
const card = document.createElement('div');
card.className = `provider-card ${accepting ? 'accepting' : 'full'}`;
card.innerHTML = `
<strong>${name}</strong>
<p>${specialty.replace('-', ' ')}</p>
<p style="font-size:12px;color:#64748b">${address}</p>
<small>${accepting ? `Next: ${nextAvailable || 'call to confirm'}` : 'Not accepting'}</small>
`;
card.addEventListener('click', () => {
map.flyTo({ center: geometry.coordinates, zoom: 15 });
});
list.appendChild(card);
});
}
Liste, sağlayıcı verileriniz hangi sırayla geliyorsa o sırayla görüntülenir. Listeyi her sağlayıcıya gerçekte ne kadar sürede ulaşıldığına göre sıralamak için backend'inizin arama başlangıç noktasını ve filtrelenmiş sağlayıcı kümesini Distance Matrix API'ye göndermesini sağlayın, ardından kartları dönen yolculuk sürelerine göre sıralayın. Aşağıdaki kontrol listesinde açıklanan nedenle bu isteği de geocoding için kullandığınız sunucu tarafı proxy üzerinden yönlendirin. Böylece istek AB içinde işlenir.
GDPR uygulama kontrol listesi
AB'deki sağlık geliştiricileri bu listeyi asgari uyumluluk kontrol listesi olarak kullanmalı:
- Geocoding için backend proxy: Hastaların posta kodları istemci tarafından üçüncü taraf API'lere gönderilmemeli.
- Arama terimleri oturum kayıtlarına yazılmamalı: Hata ayıklama için istekleri kaydediyorsanız, kayda yazmadan önce posta kodunu çıkarın.
- Gizlilik bildirimi: Uygulamanızın gizlilik bildirimi, yakındaki sağlayıcıları bulmak için konum verisinin işlendiğini, Madde 6 kapsamındaki hukuki dayanağı, arama verilerinin sağlık bilgisi ortaya koyabildiği durumlarda açık rıza gibi bir Madde 9(2) koşulunu ve saklama süresini açıklamalı.
- Konum erişimi için onay: Tarayıcının Geolocation API'sini (
navigator.geolocation.getCurrentPosition) kullanıyorsanız, tarayıcının izin penceresini tetiklemeden önce amacı açıkça belirten bir metin gösterin. - Veri yerleşimi: MapAtlas tüm API isteklerini AB içinde işler. İşleme Faaliyetleri Kayıtlarınızı hazırlarken MapAtlas DPA belgesine başvurun.
- Saklama süresi: Performans için izokron sonuçlarını veya arama koordinatlarını sunucu tarafında önbelleğe alıyorsanız, bir saklama süresi belirleyin (örneğin 24 saat) ve silme işlemini otomatikleştirin.
GDPR uyumlu harita API'leri için AB geliştirici rehberi, veri işleyen sözleşmelerini, veri yerleşimini ve onay yöntemlerini eksiksiz ele alıyor. Sağlık hizmetleri sektör sayfası ise dijital sağlık uygulamalarıyla ilgili MapAtlas özelliklerini listeliyor.
Sırada ne var
- Ücretsiz MapAtlas API anahtarı almak için kaydolun: haritalar, geocoding, rota planlama ve izokronlar için tek anahtar
- Birkaç şubeden fazlasına sahip çok klinikli ağlar için Mağaza bulucu eğitimindeki yöntemi ekleyin
- Posta kodu sorgulama, ters geocoding ve toplu sağlayıcı adresi doğrulama için Geocoding API özellikleri sayfasına göz atın
Sıkça sorulan sorular
Hasta verisi saklamasam bile doktor bulucu haritası neden GDPR kapsamında?
Bir hastanın sağlık bulucudaki arama konumu nerede yaşadığını ve dolaylı olarak hangi rahatsızlıkları olabileceğini ortaya koyar (örneğin bir posta kodu yakınında onkolog aramak). GDPR Madde 9 kapsamında konum verisinden yapılan sağlıkla ilgili çıkarımlar özel nitelikli veri sayılabilir. Profil saklamasanız bile, istemci tarafından üçüncü taraf API'lere gönderilen gerçek zamanlı geocoding istekleri, hukuki dayanak ve bilgilendirme gerektiren bir işleme kaydı oluşturur.
İzokron nedir ve sağlayıcı bulucu için neden faydalıdır?
İzokron, bir başlangıç noktasından belirli bir yolculuk süresi içinde ulaşılabilen tüm noktaları gösteren bir poligondur. Sağlık bulucuda izokron katmanı "arabayla 20 dakika içinde hangi doktorlar var?" sorusunu yanıtlar. Bu, düz mesafeden çok daha faydalı bir sorudur, çünkü yolları, hız sınırlarını ve trafik düzenini hesaba katar.
MapAtlas'ı hastalara yönelik bir NHS veya kamu sağlığı uygulamasında kullanabilir miyim?
Evet. MapAtlas, ISO 27001 sertifikalı veri merkezlerinde AB içinde barındırılır ve Madde 28 kapsamında GDPR uyumlu bir Veri İşleme Sözleşmesi sunar. Bu, kamu sektörü satın alma süreçlerinin tipik gereksinimlerini karşılar. İşlemenin AB içinde kalması, kamu sektörü ve sağlık alanındaki satın alma ekiplerinin rutin olarak sorduğu uluslararası veri aktarımı sorularını da ortadan kaldırır.
Sıkça Sorulan Sorular
Hasta verisi saklamasam bile doktor bulucu haritası neden GDPR kapsamında?
Bir hastanın sağlık bulucudaki arama konumu nerede yaşadığını ve dolaylı olarak hangi rahatsızlıkları olabileceğini ortaya koyar (örneğin bir posta kodu yakınında onkolog aramak). GDPR Madde 9 kapsamında konum verisinden yapılan sağlıkla ilgili çıkarımlar özel nitelikli veri sayılabilir. Profil saklamasanız bile, istemci tarafından üçüncü taraf API'lere gönderilen gerçek zamanlı geocoding istekleri, hukuki dayanak ve bilgilendirme gerektiren bir işleme kaydı oluşturur.
İzokron nedir ve sağlayıcı bulucu için neden faydalıdır?
İzokron, bir başlangıç noktasından belirli bir yolculuk süresi içinde ulaşılabilen tüm noktaları gösteren bir poligondur. Sağlık bulucuda izokron katmanı "arabayla 20 dakika içinde hangi doktorlar var?" sorusunu yanıtlar. Bu, düz mesafeden çok daha faydalı bir sorudur, çünkü yolları, hız sınırlarını ve trafik düzenini hesaba katar.
MapAtlas'ı hastalara yönelik bir NHS veya kamu sağlığı uygulamasında kullanabilir miyim?
Evet. MapAtlas, ISO 27001 sertifikalı veri merkezlerinde AB içinde barındırılır ve Madde 28 kapsamında GDPR uyumlu bir Veri İşleme Sözleşmesi sunar. Bu, kamu sektörü satın alma süreçlerinin tipik gereksinimlerini karşılar. İşlemenin AB içinde kalması, kamu sektörü ve sağlık alanındaki satın alma ekiplerinin rutin olarak sorduğu uluslararası veri aktarımı sorularını da ortadan kaldırır.

