Python Developer Guide
Other documentationsDemoCommunityGitHub
  • Forest Admin
  • Getting started
    • How it works
    • Quick start
      • Flask
      • Django
    • Create your agent
    • Troubleshooting
    • Migrating legacy agents
      • Pre-requisites
      • Recommendations
      • Migration steps
      • Code transformations
        • API Charts
        • Live Queries
        • Smart Charts
        • Route overrides
        • Smart Actions
        • Smart Fields
        • Smart Relationships
        • Smart Segments
  • Data Sources
    • Getting Started
      • Collection selection
      • Naming conflicts
      • Query interface and Native Queries
        • Fields and projections
        • Filters
        • Aggregations
    • Provided data sources
      • SQLAlchemy
      • Django
        • Polymorphic relationships
    • Write your own
      • Translation strategy
        • Structure declaration
        • Capabilities declaration
        • Read implementation
        • Write implementation
        • Intra-data source Relationships
      • Contribute
  • Agent customization
    • Getting Started
    • Actions
      • Scope and context
      • Result builder
      • Static Forms
      • Widgets in Forms
      • Dynamic Forms
      • Form layout customization
      • Related data invalidation
    • Charts
      • Value
      • Objective
      • Percentage
      • Distribution
      • Leaderboard
      • Time-based
    • Fields
      • Add fields
      • Move, rename and remove fields
      • Override binary field mode
      • Override writing behavior
      • Override filtering behavior
      • Override sorting behavior
      • Validation
    • Hooks
      • Collection hook
      • Collection override
    • Pagination
    • Plugins
      • Write your own
    • Relationships
      • To a single record
      • To multiple records
      • Computed foreign keys
      • Under the hood
    • Search
    • Segments
  • Frontend customization
    • Smart Charts
      • Create a table chart
      • Create a bar chart
      • Create a cohort chart
      • Create a density map
    • Smart Views
      • Create a Map view
      • Create a Calendar view
      • Create a Shipping view
      • Create a Gallery view
      • Create a custom tinder-like validation view
      • Create a custom moderation view
  • Deploying to production
    • Environments
      • Deploy on AWS
      • Deploy on Heroku
      • Deploy on GCP
      • Deploy on Ubuntu
    • Development workflow
    • Using branches
    • Deploying your changes
    • Forest Admin CLI commands
      • init
      • login
      • branch
      • switch
      • set-origin
      • push
      • environments:create
      • environments:reset
      • deploy
  • Under the hood
    • .forestadmin-schema.json
    • Data Model
      • Typing
      • Relationships
    • Security & Privacy
Powered by GitBook
On this page

Was this helpful?

  1. Frontend customization
  2. Smart Views

Create a Map view

PreviousSmart ViewsNextCreate a Calendar view

Last updated 1 year ago

Was this helpful?

This is the official documentation of the forestadmin-agent-django and forestadmin-agent-flask Python agents.

The example below shows how to display a map view:

component.js
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { scheduleOnce } from '@ember/runloop';
import { action } from '@ember/object';
import { observes } from '@ember-decorators/object';
import { tracked } from '@glimmer/tracking';
import { guidFor } from '@ember/object/internals';
import {
  triggerSmartAction,
  deleteRecords,
  getCollectionId,
  loadExternalStyle,
  loadExternalJavascript,
} from 'client/utils/smart-view-utils';

export default class extends Component {
  @service router;
  @service store;

  @tracked map = null;
  @tracked loaded = false;

  constructor(...args) {
    super(...args);

    this.loadPlugin();
  }

  get mapId() {
    return `map-${guidFor(this)}`;
  }

  async loadPlugin() {
    loadExternalStyle('//cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.4/leaflet.css');
    loadExternalStyle(
      '//cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css',
    );

    await loadExternalJavascript(
      '//cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.4/leaflet.js',
    );
    await loadExternalJavascript(
      '//cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js',
    );

    this.loaded = true;
    this.displayMap();
  }

  @action
  displayMap() {
    if (!this.loaded) {
      return;
    }

    if (this.map) {
      this.map.off();
      this.map.remove();
      this.map = null;
    }

    const markers = [];

    this.args.records?.forEach(function (record) {
      markers.push([
        record.get('forest-lat'),
        record.get('forest-lng'),
        record.get('id'),
      ]);
    });

    this.map = new L.Map(this.mapId);

    const osmUrl =
      'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png';
    const osmAttrib =
      '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> &copy; <a href="http://cartodb.com/attributions">CartoDB</a>';
    const osm = new L.TileLayer(osmUrl, { attribution: osmAttrib });
    const drawnItems = new L.FeatureGroup();
    this.map.addLayer(drawnItems);

    const drawControl = new L.Control.Draw({
      draw: {
        polygon: false,
        polyline: false,
        rectangle: false,
        circle: false,
        circlemarker: false,
        marker: true,
      },
      edit: {
        featureGroup: drawnItems,
      },
    });

    this.map.setView(new L.LatLng(48.8566, 2.3522), 2);
    this.map.addLayer(osm);
    this.map.addControl(drawControl);

    this.map.on(L.Draw.Event.CREATED, event => {
      const { layer, layerType: type } = event;

      if (type === 'marker') {
        const coordinates = event.layer.getLatLng();
        const newRecord = this.store.createRecord('forest_delivery', {
          'forest-is_delivered': false,
          'forest-lng': coordinates.lng,
          'forest-lat': coordinates.lat,
        });

        newRecord.save().then(savedRecord => {
          layer.on('click', () => {
            this.router.transitionTo(
              'project.rendering.data.collection.list.view-edit.details',
              this.args.collection.id,
              savedRecord.id,
            );
          });
        });
      }

      this.map.addLayer(layer);
    });

    this.addMarkers(markers);
  }

  addMarker(marker) {
    const lat = Number.parseFloat(marker[0]);
    const long = Number.parseFloat(marker[1]);

    const recordId = marker[2];
    marker = L.marker([lat, long]).addTo(this.map);

    marker.on('click', () => {
      this.router.transitionTo(
        'project.rendering.data.collection.list.view-edit.details',
        this.args.collection.id,
        recordId,
      );
    });
  }

  addMarkers(markers) {
    markers.forEach(marker => this.addMarker(marker));
  }

  @action
  triggerSmartAction(...args) {
    return triggerSmartAction(this, ...args);
  }

  @action
  deleteRecords(...args) {
    return deleteRecords(this, ...args);
  }
}
style.css
.c-map {
  width: 100%;
  height: 100%;
  z-index: 4;
}

#map {
  width: 100%;
  z-index: 4;
}

.c-smart-view {
  display: flex;
  white-space: normal;
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  top: 0;
  background-color: var(--color-beta-surface);
}

.c-smart-view__content {
  margin: auto;
  text-align: center;
  color: var(--color-beta-on-surface_medium);
}

.c-smart-view_icon {
  margin-bottom: 32px;
  font-size: 32px;
}
template.hbs
<div
  id={{this.mapId}}
  class='c-map'
  {{did-insert this.displayMap}}
  {{did-update this.displayMap @records}}
></div>