Реалізація інтерактивних карт (Google Maps/Яндекс.Карти) на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Реалізація інтерактивних карт (Google Maps/Яндекс.Карти) на сайті
Середня
~3-5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Реалізація інтерактивних карт (Google Maps/Яндекс.Карти) на сайті

Вибір між Google Maps та Яндекс.Картами визначається аудиторією: для російського ринку Яндекс покриває деталі, недоступні в Google. Обидві платформи потребують API-ключів, білінгу та уваги до продуктивності.

Google Maps: базова інтеграція

<!-- Завантажуємо API асинхронно -->
<script>
(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})
({key: "YOUR_API_KEY", v: "weekly"});
</script>
async function initMap(containerId: string) {
  const { Map, InfoWindow } = await google.maps.importLibrary('maps') as google.maps.MapsLibrary
  const { AdvancedMarkerElement } = await google.maps.importLibrary('marker') as google.maps.MarkerLibrary

  const map = new Map(document.getElementById(containerId)!, {
    center: { lat: 55.7558, lng: 37.6173 },  // Москва
    zoom: 14,
    mapId: 'YOUR_MAP_ID',   // для AdvancedMarker та Cloud Styling
    gestureHandling: 'cooperative',  // Ctrl+scroll для зуму — не заважає скролу сторінки
    disableDefaultUI: false,
    zoomControl: true,
    mapTypeControl: false,
    streetViewControl: false,
    fullscreenControl: true,
    restriction: {
      latLngBounds: { north: 85, south: -85, west: -180, east: 180 },
      strictBounds: false,
    },
  })

  return { map, InfoWindow, AdvancedMarkerElement }
}

Кастомні маркери та кластеризація

import { MarkerClusterer } from '@googlemaps/markerclusterer'

interface Location {
  lat: number
  lng: number
  title: string
  description: string
  category: string
}

async function addMarkersWithClustering(map: google.maps.Map, locations: Location[]) {
  const { AdvancedMarkerElement } = await google.maps.importLibrary('marker') as google.maps.MarkerLibrary
  const infoWindow = new google.maps.InfoWindow()

  const markers = locations.map(loc => {
    // Кастомний DOM-елемент замість дефолтного маркера
    const pin = document.createElement('div')
    pin.className = `map-pin map-pin--${loc.category}`
    pin.innerHTML = `<span class="map-pin__label">${loc.title}</span>`

    const marker = new AdvancedMarkerElement({
      map,
      position: { lat: loc.lat, lng: loc.lng },
      content: pin,
      title: loc.title,
    })

    marker.addListener('click', () => {
      infoWindow.setContent(`
        <div class="map-popup">
          <h3>${loc.title}</h3>
          <p>${loc.description}</p>
          <a href="/locations/${loc.title}" target="_blank">Детальніше</a>
        </div>
      `)
      infoWindow.open(map, marker)
    })

    return marker
  })

  // Кластеризація близьких маркерів
  new MarkerClusterer({
    map,
    markers,
    algorithm: new SuperClusterAlgorithm({ radius: 60 }),
    renderer: {
      render: ({ count, position }) => new google.maps.Marker({
        label: { text: String(count), color: '#fff', fontSize: '12px' },
        position,
        icon: { url: '/icons/cluster.svg', scaledSize: new google.maps.Size(40, 40) },
        zIndex: 1000 + count,
      }),
    },
  })
}

Яндекс.Карти 3.0 (сучасний API)

npm install @yandex/ymaps3-types
// Ініціалізація через module import (Яндекс.Карти 3.0)
async function initYandexMap(containerId: string) {
  await ymaps3.ready

  const { YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer } = ymaps3

  const map = new YMap(
    document.getElementById(containerId)!,
    {
      location: {
        center: [37.6173, 55.7558],  // У Яндексі порядок: [lng, lat]
        zoom: 14,
      },
      behaviors: ['drag', 'scrollZoom', 'pinchZoom'],
    }
  )

  map.addChild(new YMapDefaultSchemeLayer({ theme: 'light' }))
  map.addChild(new YMapDefaultFeaturesLayer())

  return map
}

// Маркери в Яндекс.Картах 3.0
async function addYandexMarkers(map: any, locations: Location[]) {
  const { YMapMarker, YMapClusterer, clusterByGrid } = await ymaps3.import('@yandex/[email protected]')

  const getMarkerElement = (loc: Location) => {
    const el = document.createElement('div')
    el.className = `ymap-pin ymap-pin--${loc.category}`
    el.textContent = loc.title
    return el
  }

  const markers = locations.map(loc => ({
    id: loc.title,
    geometry: { type: 'Point', coordinates: [loc.lng, loc.lat] },
    properties: loc,
  }))

  const clusterer = new YMapClusterer({
    method: clusterByGrid({ gridSize: 64 }),
    features: markers,
    marker: (feature) => new YMapMarker(
      { coordinates: feature.geometry.coordinates },
      getMarkerElement(feature.properties)
    ),
    cluster: (coordinates, features) => {
      const el = document.createElement('div')
      el.className = 'ymap-cluster'
      el.textContent = String(features.length)
      return new YMapMarker({ coordinates }, el)
    },
  })

  map.addChild(clusterer)
}

Продуктивність: lazy-ініціалізація

Підключення Google Maps SDK добавляє ~240KB JS. Завантажуємо лише при взаємодії:

import { useState, useRef } from 'react'

export function LazyMap({ center, zoom = 14 }: { center: { lat: number; lng: number }; zoom?: number }) {
  const [loaded, setLoaded] = useState(false)
  const containerRef = useRef<HTMLDivElement>(null)

  function loadMap() {
    if (loaded) return
    setLoaded(true)
  }

  return (
    <div className="map-wrapper" style={{ position: 'relative', height: 400 }}>
      {!loaded && (
        <div
          className="map-placeholder"
          onClick={loadMap}
          style={{
            position: 'absolute', inset: 0,
            background: `url(https://maps.googleapis.com/maps/api/staticmap?center=${center.lat},${center.lng}&zoom=${zoom}&size=800x400&key=${API_KEY}) center/cover`,
            cursor: 'pointer',
          }}
        >
          <button className="map-load-btn" aria-label="Завантажити інтерактивну карту">
            Натисніть для завантаження карти
          </button>
        </div>
      )}
      {loaded && (
        <div ref={containerRef} style={{ height: '100%' }} id="map" />
      )}
    </div>
  )
}

Static Maps API — дешевше та швидше для статичного превью.

Геокодування: адреса → координати

// Google Geocoding API
async function geocode(address: string): Promise<{ lat: number; lng: number }> {
  const { Geocoder } = await google.maps.importLibrary('geocoding') as google.maps.GeocodingLibrary
  const geocoder = new Geocoder()

  const result = await geocoder.geocode({ address, region: 'ua', language: 'uk' })
  if (!result.results.length) throw new Error(`Address not found: ${address}`)

  const { lat, lng } = result.results[0].geometry.location
  return { lat: lat(), lng: lng() }
}

// Серверне геокодування через Laravel (не витрачаємо клієнтські запити)
// composer require spatie/geocoder
class GeoController extends Controller
{
    public function geocode(Request $request): JsonResponse
    {
        $address = $request->validate(['address' => 'required|string'])['address'];

        $result = Geocoder::getCoordinatesForAddress($address);

        return response()->json([
            'lat' => $result['lat'],
            'lng' => $result['lng'],
            'accuracy' => $result['accuracy'],
        ]);
    }
}

Маршрутизація та маршрути

// Відображення маршруту між двома точками
async function showRoute(map: google.maps.Map, origin: string, destination: string) {
  const { DirectionsService, DirectionsRenderer, TravelMode } = await google.maps.importLibrary('routes') as any

  const service = new DirectionsService()
  const renderer = new DirectionsRenderer({
    map,
    suppressMarkers: false,
    polylineOptions: { strokeColor: '#6366f1', strokeWeight: 4 },
  })

  const result = await service.route({
    origin,
    destination,
    travelMode: TravelMode.DRIVING,
    region: 'ua',
    language: 'uk',
    provideRouteAlternatives: true,
  })

  renderer.setDirections(result)

  // Інформація про маршрут
  const route = result.routes[0].legs[0]
  return {
    distance: route.distance?.text,
    duration: route.duration?.text,
  }
}

CSP для Google Maps

// Laravel: дозволяємо Google Maps у Content Security Policy
$csp = [
    "script-src 'self' https://maps.googleapis.com https://maps.gstatic.com",
    "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
    "img-src 'self' data: https://*.googleapis.com https://*.gstatic.com",
    "connect-src 'self' https://maps.googleapis.com",
    "frame-src https://www.google.com",
];

Терміни

Статична карта з маркером та popup — 1 день. З кластеризацією, lazy-загруженням, геокодуванням та кастомними стилями — 2–3 дні. З маршрутами, пошуком адрес, кількома типами маркерів та мобільною оптимізацією — 1 тиждень.