← Back to list

Building an Interactive India Map in Angular with OpenLayers — Plus Live Weather on States and…

Maps are one of those features that instantly make a dashboard feel “real.” In this post, we’ll build an Angular application that renders…

Deepak · 2026-08-13 03:32 · 50 claps · 5.9 min read
#angular #angularjs #openlayers #maps #web-development
Open on Medium ↗
Wiki topics: 🌐 · Web Development 🌍 · Earth Science 🎬 · Film & Television

Building an Interactive India Map in Angular with OpenLayers — Plus Live Weather on States and Cities

Maps are one of those features that instantly make a dashboard feel “real.” In this post, we’ll build an Angular application that renders an interactive map of India using OpenLayers, highlights states, plots major cities, and shows live weather data when you click on a state or city marker.

By the end, you’ll have:

  • An Angular app rendering a India-focused OpenLayers map
  • India state boundaries loaded from GeoJSON
  • City markers with click-to-view weather popups
  • A weather service pulling live data from a free weather API

1. Why OpenLayers (and not Leaflet or Google Maps)?

OpenLayers is a great fit here because:

  • It’s open-source and free, with no API key required for the base map
  • It handles vector layers (GeoJSON, states/districts) natively and efficiently
  • It gives fine-grained control over styling, projections, and interactions — useful for a country-specific map like India where you often want custom state boundaries, not just markers

If your use case is simpler (just markers, no polygon boundaries), Leaflet is lighter. But since we want state-level shading and interaction, OpenLayers’ vector layer handling is the better tool.

2. Project Setup

Start a fresh Angular project and install OpenLayers:

ng new india-weather-map --routing=false --style=scss
cd india-weather-map
npm install ol

ol is the OpenLayers npm package. No additional Angular wrapper library is required — OpenLayers works fine directly inside a component using its native JS/TS API.

3. Getting India State Boundary Data (GeoJSON)

You need a GeoJSON file with India’s state boundaries. A few good sources:

  • datameet/maps — community-maintained Indian administrative boundary data
  • Survey of India — official but harder to work with programmatically
  • Various public GitHub repos hosting india_states.geojson

Download a state-level GeoJSON and place it in src/assets/india-states.geojson. Each feature typically looks like:

{
  "type": "Feature",
  "properties": { "st_nm": "Maharashtra" },
  "geometry": { "type": "Polygon", "coordinates": [ ... ] }
}

We’ll use the st_nm property to label states and match weather data.

4. Building the Map Component

Generate a component to house the map:

ng generate component map

4.1 Basic Map Setup

// map.component.ts
import { AfterViewInit, Component, OnDestroy } from '@angular/core';
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import OSM from 'ol/source/OSM';
import GeoJSON from 'ol/format/GeoJSON';
import { Style, Fill, Stroke, Circle as CircleStyle, Text } from 'ol/style';
import { fromLonLat } from 'ol/proj';
import Feature from 'ol/Feature';
import Point from 'ol/geom/Point';
import { WeatherService } from '../weather.service';
@Component({
  selector: 'app-map',
  templateUrl: './map.component.html',
  styleUrls: ['./map.component.scss']
})
export class MapComponent implements AfterViewInit, OnDestroy {
  map!: Map;
  selectedInfo: any = null;
  // A few major Indian cities to plot
  cities = [
    { name: 'New Delhi', lat: 28.6139, lon: 77.2090 },
    { name: 'Mumbai', lat: 19.0760, lon: 72.8777 },
    { name: 'Bengaluru', lat: 12.9716, lon: 77.5946 },
    { name: 'Kolkata', lat: 22.5726, lon: 88.3639 },
    { name: 'Chennai', lat: 13.0827, lon: 80.2707 },
    { name: 'Hyderabad', lat: 17.3850, lon: 78.4867 },
    { name: 'Jaipur', lat: 26.9124, lon: 75.7873 },
    { name: 'Lucknow', lat: 26.8467, lon: 80.9462 }
  ];
  constructor(private weatherService: WeatherService) {}
  ngAfterViewInit(): void {
    this.initMap();
  }
  private initMap(): void {
    // Base OSM tile layer
    const baseLayer = new TileLayer({ source: new OSM() });
    // India state boundaries layer
    const stateSource = new VectorSource({
      url: 'assets/india-states.geojson',
      format: new GeoJSON()
    });
    const stateLayer = new VectorLayer({
      source: stateSource,
      style: (feature) => new Style({
        fill: new Fill({ color: 'rgba(255, 153, 51, 0.08)' }), // subtle saffron tint
        stroke: new Stroke({ color: '#0b5394', width: 1.2 }),
        text: new Text({
          text: feature.get('st_nm'),
          font: '10px sans-serif',
          fill: new Fill({ color: '#333' })
        })
      })
    });
    // City markers layer
    const cityFeatures = this.cities.map(city => {
      const feature = new Feature({
        geometry: new Point(fromLonLat([city.lon, city.lat])),
        name: city.name,
        lat: city.lat,
        lon: city.lon
      });
      feature.setStyle(new Style({
        image: new CircleStyle({
          radius: 6,
          fill: new Fill({ color: '#d32f2f' }),
          stroke: new Stroke({ color: '#fff', width: 2 })
        })
      }));
      return feature;
    });
    const cityLayer = new VectorLayer({
      source: new VectorSource({ features: cityFeatures })
    });
    // Map centered on India
    this.map = new Map({
      target: 'india-map',
      layers: [baseLayer, stateLayer, cityLayer],
      view: new View({
        center: fromLonLat([78.9629, 22.5937]), // geographic center of India
        zoom: 4.6
      })
    });
    // Click handler: state polygons or city points
    this.map.on('click', (evt) => {
      this.map.forEachFeatureAtPixel(evt.pixel, (feature) => {
        const cityName = feature.get('name');
        const stateName = feature.get('st_nm');
        if (cityName) {
          this.showWeather(cityName, feature.get('lat'), feature.get('lon'));
        } else if (stateName) {
          // Use state capital lookup or geometry centroid for weather
          this.showWeatherForState(stateName);
        }
      });
    });
  }
  private showWeather(name: string, lat: number, lon: number): void {
    this.weatherService.getWeatherByCoords(lat, lon).subscribe(data => {
      this.selectedInfo = {
        name,
        temp: data.main.temp,
        description: data.weather[0].description,
        humidity: data.main.humidity,
        wind: data.wind.speed
      };
    });
  }
  private showWeatherForState(stateName: string): void {
    // Map state name -> representative city/coords for a quick lookup,
    // or reverse-geocode the polygon centroid
    const coords = this.stateCentroidLookup(stateName);
    if (coords) {
      this.showWeather(stateName, coords.lat, coords.lon);
    }
  }
  private stateCentroidLookup(stateName: string): { lat: number; lon: number } | null {
    // Simplified lookup table; extend with all states as needed
    const table: Record<string, { lat: number; lon: number }> = {
      'Maharashtra': { lat: 19.7515, lon: 75.7139 },
      'Karnataka': { lat: 15.3173, lon: 75.7139 },
      'Uttar Pradesh': { lat: 26.8467, lon: 80.9462 },
      'Rajasthan': { lat: 27.0238, lon: 74.2179 }
      // ... add remaining states
    };
    return table[stateName] || null;
  }
  ngOnDestroy(): void {
    this.map?.setTarget(undefined);
  }
}

4.2 Template

<!-- map.component.html -->
<div id="india-map" class="map-container"></div>
<div class="weather-panel" *ngIf="selectedInfo">
  <h3>{{ selectedInfo.name }}</h3>
  <p>🌡️ {{ selectedInfo.temp }}°C</p>
  <p>☁️ {{ selectedInfo.description }}</p>
  <p>💧 Humidity: {{ selectedInfo.humidity }}%</p>
  <p>💨 Wind: {{ selectedInfo.wind }} m/s</p>
</div>

4.3 Styles

// map.component.scss
.map-container {
  width: 100%;
  height: 600px;
}
.weather-panel {
  position: absolute;
  top: 20px;
  right: 20px;
  background: white;
  padding: 16px 20px;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.15);
  min-width: 200px;
}

5. The Weather Service

We’ll use the free tier of OpenWeatherMap — sign up for an API key, then wire up a simple service.

// weather.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class WeatherService {
  private apiKey = 'YOUR_OPENWEATHERMAP_API_KEY';
  private baseUrl = 'https://api.openweathermap.org/data/2.5/weather';
  constructor(private http: HttpClient) {}
  getWeatherByCoords(lat: number, lon: number): Observable<any> {
    const url = `${this.baseUrl}?lat=${lat}&lon=${lon}&appid=${this.apiKey}&units=metric`;
    return this.http.get(url);
  }
  getWeatherByCity(cityName: string): Observable<any> {
    const url = `${this.baseUrl}?q=${cityName},IN&appid=${this.apiKey}&units=metric`;
    return this.http.get(url);
  }
}

Don’t forget to import HttpClientModule in your root module (or provide HttpClient via provideHttpClient() if using standalone components).

// app.config.ts (standalone) or app.module.ts
import { provideHttpClient } from '@angular/common/http';
// in providers array:
provideHttpClient()

Note on API keys: never hardcode production keys in client-side code that ships to users. For a real app, proxy weather requests through a small backend endpoint so the key stays server-side.

6. Coloring States by Temperature (Choropleth Style)

A nice enhancement: instead of a flat color, shade each state based on its current temperature — turning the map into a live choropleth.

private colorByTemp(temp: number): string {
  if (temp >= 35) return 'rgba(211, 47, 47, 0.5)';   // hot: red
  if (temp >= 25) return 'rgba(255, 152, 0, 0.5)';    // warm: orange
  if (temp >= 15) return 'rgba(255, 235, 59, 0.5)';   // mild: yellow
  return 'rgba(33, 150, 243, 0.5)';                   // cool: blue
}

To apply this, fetch weather for each state’s centroid on load, store it in a map keyed by st_nm, and reference that map inside the stateLayer's style function instead of a fixed fill color. Since OpenWeatherMap's free tier has rate limits, batch these calls with a short delay between requests, or cache results for a few minutes.

7. Performance Tips

  • Simplify GeoJSON geometry. Full-resolution state boundaries can be several MB. Run them through mapshaper to simplify before shipping to assets/.
  • Debounce weather calls. Don’t fetch weather on every map pan/zoom — only on explicit click.
  • Cache weather responses client-side (even a simple in-memory map with a 10-minute TTL) to avoid hammering the API and hitting rate limits.
  • Lazy-load the map component if it’s not on your landing route, since OpenLayers adds noticeable bundle size.

8. Wrapping Up

With OpenLayers’ vector layer support and a free weather API, you can build a fully interactive, data-driven map of India in a surprisingly small amount of Angular code. From here, natural next steps include:

  • District-level boundaries instead of just states
  • A search box to jump to any city
  • Historical weather trends via a charting library on click
  • Switching the base tile layer to a satellite or terrain view

The full pattern — GeoJSON vector layer + point layer + click-driven service call — generalizes well beyond weather too (population data, election results, air quality index, etc.).

Have questions about extending this further — like adding district boundaries or a live IMD (India Meteorological Department) data feed instead of OpenWeatherMap? Happy to dig into either.


메타데이터
post_id
daddaa0607bc
slug
building-an-interactive-india-map-in-angular-with-openlayers-plus-live-weather-on-states-and-daddaa0607bc
url
https://medium.com/@deepakjais/building-an-interactive-india-map-in-angular-with-openlayers-plus-live-weather-on-states-and-daddaa0607bc
canonical_url
https://medium.com/@deepakjais/building-an-interactive-india-map-in-angular-with-openlayers-plus-live-weather-on-states-and-daddaa0607bc
author_url
https://medium.com/@deepakjais
status
ok
fetched_at
2026-08-29 04:53:14