How to add custom markers using google maps directions api in react js - google-maps-api-3

Tech Stack - Google Map Directions Api, React JS, etc.
Problem Statement - Using google maps direction Api's i am drawing 3 pins i.e. origin, waypoints and destination. I am able to plot all these of these but unable to add custom markers to each of these. Any help/suggestion shall be really appreciated: -
Code Snippet: -
import React, { Component } from "react";
import {
withGoogleMap,
withScriptjs,
GoogleMap,
DirectionsRenderer
} from "react-google-maps";
class Map extends Component {
state = {
directions: null
};
componentDidMount() {
const directionsService = new google.maps.DirectionsService();
const origin = { lat: 40.756795, lng: -73.954298, icon: 'https://toppng.com/uploads/preview/map-marker-icon-600x-map-marker-11562939743ayfahlvygl.png' };
const destination = { lat: 41.756795, lng: -78.954298, icon: 'https://toppng.com/uploads/preview/map-marker-icon-600x-map-marker-11562939743ayfahlvygl.png' };
directionsService.route({
origin: origin,
destination: destination,
waypoints: [{
location: new google.maps.LatLng(42.756795, -78.954298, 'https://toppng.com/uploads/preview/map-marker-icon-600x-map-marker-11562939743ayfahlvygl.png'),
stopover: false
}],
travelMode: google.maps.TravelMode.DRIVING
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result
});
} else {
console.error(`error fetching directions ${result}`);
}
}
);
}
render() {
const GoogleMapExample = withGoogleMap(props => (
<GoogleMap
defaultCenter={{ lat: 40.756795, lng: -73.954298 }}
defaultZoom={13}
>
<DirectionsRenderer
directions={this.state.directions}
/>
</GoogleMap>
));
return (
<div>
<GoogleMapExample
containerElement={<div style={{ height: `500px`, width: "500px" }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
</div>
);
}
}
export default Map;
Thanks

react-google-maps has a Marker component that you can use.
<GoogleMap
defaultCenter={{ lat: 40.756795, lng: -73.954298 }}
defaultZoom={13}
>
<DirectionsRenderer
directions={this.state.directions}
/>
<Marker location={{ lat: x, lng: y }} />
</GoogleMap>

Related

Can I load the component template dynamically

Is it possible to load the template from server (include components) dynamically? Or can I change the template before it rendered?
I would like to let user to store their own form template into a database and generate the form according to the template-id.
I tried to change the this.$options.template, but it seems like only work for vue2.
<!-- static/myproj/js/my-field.vue -->
<template>
<label :for="name+'Fld'" v-html="title"></label>
<input :name="name" :type="text" :value="value" :id="name+'Fld'"/>
</template>
<script>
export default{
props: {
name: {type:String, required:true},
value: {type:String, required:false, default:''},
type: {type:String, required:true},
title: {type:String, required:false, default:'Field: '},
},
data: function(){ return {}; },
}
</script>
// index.vue
const loadVueModuleOpts= {
moduleCache: {vue: Vue},
async getFile(url) {
const res = await fetch(url);
if ( !res.ok )
throw Object.assign(new Error(res.statusText + ' ' + url), { res });
return {
getContentData: asBinary => asBinary ? res.arrayBuffer() : res.text(),
}
},
};
export default{
props: {
id: {required:true, type:String, default:'abcdefg'},
},
data: function(){
this.loadSource();
return {
source: null,
target: null,
};
},
template: '<div>I\'m here to be replaced.</div>',
created: async function(){
this.$options.template=await axios.get(`/api/template/${id}`).then(resp=>resp.data);
},
components: {
'my-field': Vue.defineAsyncComponent( ()=>loadModule('/static/myproj/js/my-field.vue', loadVueModuleOpts)),
}
<!-- server response for /api/template/abcdefg -->
<form action="POST">
<my-field name="name" title="Your Name: " type="text"/>
<my-field name="email" title="Email: " type="email"/>
<input type="submit"/><input type="reset"/>
</form>
Thanks.
Finally, I got the solution. According to Vue3: How to use Vue.compile in Vue3, we can render the template directly by Vue3 like this:
// index.vue
import { h, compile } from 'vue';
const loadVueModuleOpts= {
moduleCache: {vue: Vue},
async getFile(url) {
const res = await fetch(url);
if ( !res.ok )
throw Object.assign(new Error(res.statusText + ' ' + url), { res });
return {
getContentData: asBinary => asBinary ? res.arrayBuffer() : res.text(),
}
},
};
export default{
props: {
id: {required:true, type:String, default:'abcdefg'},
},
data: function(){
this.loadSource();
return {
source: null,
target: null,
};
},
// Magic here
render: function(){
if(this.target)
return h(compile(this.target).bind(this));
return h('div', 'Loading...');
},
created: async function(){
this.$options.template=await axios.get(`/api/template/${id}`).then(resp=>resp.data);
},
components: {
'my-field': Vue.defineAsyncComponent( ()=>loadModule('/static/myproj/js/my-field.vue', loadVueModuleOpts)),
},
}

Convert options api to composition api for vue3 - v-model binding and watch

I have the following working code for a search input using options API for component data, watch and methods, I am trying to convert that to the composition api.
I am defining props in <script setup> and also a onMounted function.
<template>
<label for="search" class="hidden">Search</label>
<input
id="search"
ref="search"
v-model="search"
class="border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md shadow-sm h-9 w-1/2"
:class="{ 'transition-border': search }"
autocomplete="off"
name="search"
placeholder="Search"
type="search"
#keyup.esc="search = null"
/>
</template>
<script setup>
import {onMounted} from "vue";
const props = defineProps({
routeName: String
});
onMounted(() => {
document.getElementById('search').focus()
});
</script>
<!--TODO convert to composition api-->
<script>
import { defineComponent } from "vue";
export default defineComponent({
data() {
return {
// page.props.search will come from the backend after search has returned.
search: this.$inertia.page.props.search || null,
};
},
watch: {
search() {
if (this.search) {
// if you type something in the search input
this.searchMethod();
} else {
// else just give us the plain ol' paginated list - route('stories.index')
this.$inertia.get(route(this.routeName));
}
},
},
methods: {
searchMethod: _.debounce(function () {
this.$inertia.get(
route(this.routeName),
{ search: this.search }
);
}, 500),
},
});
</script>
What I am trying to do is convert it to the composition api. I have tried the following but I can't get it to work at all.
let search = ref(usePage().props.value.search || null);
watch(search, () => {
if (search.value) {
// if you type something in the search input
searchMethod();
} else {
// else just give us the plain ol' paginated list - route('stories.index')
Inertia.get(route(props.routeName));
}
});
function searchMethod() {
_.debounce(function () {
Inertia.get(
route(props.routeName),
{search: search}
);
}, 500)
}
Any help or pointers in how to convert what is currently in <script> into <script setup> would be greatly appreciated thanks.
I managed to get this working with the below!
<script setup>
import {onMounted, ref} from "vue";
import {Inertia} from "#inertiajs/inertia";
const props = defineProps({
route_name: {
type: String,
required: true
},
search: {
type: String,
default: null
}
});
const search = ref(props.search);
onMounted(() => {
search.value.focus();
search.value.addEventListener('input', () => {
if (search.value.value) {
searching();
} else {
Inertia.get(route(props.route_name));
}
});
});
const searching = _.debounce(function() {
Inertia.get(route(props.route_name), {search: search.value.value});
}, 500);
</script>

Problem with CORS. Google directions API GET request | vue.js

I currently work on vue.js project.
The app goal is to check distance between 2 localisations, then show route on the map and calculate cost of transport which is based on the distance.
I use google directions api, axios for get request.
Problem is that, because of CORS, get request gives me an error (I run this app locally).
I already tried chrome CORS plugin, but problem still exists.
Do You have any solutions or just idea how to solve this problem?
Thank You in advance.
P.S.
Code below
import axios from 'axios';
const directionsApi = 'https://maps.googleapis.com/maps/api/directions/json?';
const apiKey = '&key=trust_me_the_key_is_valid';
export default {
name: 'FirstForm',
data() {
return {
fromValue: '',
toValue: '',
distance: '',
};
},
methods: {
handleFromToInput: function () {
const fromTo = `origin=${this.fromValue}&destination=${this.toValue}`;
axios.get(`${directionsApi}${fromTo}${apiKey}`)
.then((response) => {
// this.distance = response.routes[0].legs[0].distance.text;
console.log(response.routes[0].legs[0].distance.text);
})
.catch((error) => {
console.log(error);
});
},
},
};
Similar here
If you use Javascript API way to do,
Create an account on Google Maps Platform
Open Vue Project
Make a js file (src/utils/gmap.js)
// src/utils/gmaps.js
const API_KEY = 'XXXXXYOURAPIKEYXXXX';
const CALLBACK_NAME = 'gmapsCallback';
let initialized = !!window.google;
let resolveInitPromise;
let rejectInitPromise;
const initPromise = new Promise((resolve, reject) => {
resolveInitPromise = resolve;
rejectInitPromise = reject;
});
export default function init() {
if (initialized) return initPromise;
initialized = true;
window[CALLBACK_NAME] = () => resolveInitPromise(window.google);
const script = document.createElement('script');
script.async = true;
script.defer = true;
script.src = `https://maps.googleapis.com/maps/api/js?key=${API_KEY}&callback=${CALLBACK_NAME}`;
script.onerror = rejectInitPromise;
document.querySelector('head').appendChild(script);
return initPromise;
}
In view js (src/views/MyMap.vue)
<template>
<div class="my-map">
My Map
<div id="map"></div>
</div>
</template>
<script>
import gmapsInit from '#/utils/gmaps';
export default {
name: 'myMap',
components: {
},
data () {
return {
}
},
computed: {
},
async mounted() {
try {
const google = await gmapsInit();
var map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});
} catch (error) {
console.error(error);
}
},
methods:{
}
}
</script>
<style>
#map{
width:400px;
height:400px;
}
</style>
Ref on
Using the Google Maps API with Vue.js
Maps JavaScript API, Hello World
I've found solution. Maybe it's not best but it works.
I've used cors-anywhere proxy.
https://cors-anywhere.herokuapp.com/${directionsApi}${fromTo}${apiKey}

Dynamically add, update, delete markers in react-google-maps

Looking at the docs about react-google-maps, I was able to hack together some code to render a map with static data. Now I need to make changes to the map based on new data coming in from API or periodic updates and don't see any talk about how to do this.
To make the initial application, I did an "npx create-react-app xxx" to create an app and then added the necessary npm packages for react-google-maps. Here's basic code:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import * as serviceWorker from './serviceWorker';
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from "react-google-maps"
let markers = []
function createMarkers(numMarkers){
for(let i = 0; i < numMarkers; i++){
markers.push(<Marker key={i} position={{ lat: 45.123 + (i * 0.005), lng: -75.987 }} />)
}
return markers;
}
setInterval(function(){
if(markers.length > 0 && markers[0].props.position){
let marker = markers[0];
//debugger;
let position = marker.props.position;
position.lat = position.lat - 0.005;
position.lng = position.lng - 0.005;
marker.props.position = position;
}
}, 1000)
const MyGreatMap = withScriptjs( withGoogleMap(props => <GoogleMap
defaultZoom={14}
defaultCenter={{ lat: 45.123, lng: -75.978 }}
>
{createMarkers(10)}
</GoogleMap>));
ReactDOM.render(
<MyGreatMap
googleMapURL="https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=AIzaSyDS-TFZqRfUx9xPXTJrPH6eml-gGo-btZ0"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `800px` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>,
document.getElementById('root'));
When I update the props, nothing happens. I'm sure that's wrong and it should be updating state somehow, but the lat/lng info in the markers are props.
What is the best way to update things in react-google-maps? Google maps in general is a very javascript intensive API so I don't know if it can work "the react way". What's the best way to make changes to react-google-maps to get them to efficiently re-render with changes?
I'm sure that's wrong and it should be updating state somehow, but the
lat/lng info in the markers are props.
that's right, markers needs to moved to local state, For that matter a component could be introduced which accepts markers props as initial state and then state updated like this:
class MapWithMarkers extends React.Component {
constructor(props) {
super(props);
this.state= {markers: this.props.markers}; //1.initialize initial state from props
}
updateMarker() {
this.setState(prevState => {
const markers = [...prevState.markers];
markers[index] = {lat: <val>, lng: <val>};
return {markers};
})
}
componentDidMount() {
this.intervalId = setInterval(this.updateMarker.bind(this), 1000);
}
componentWillUnmount(){
clearInterval(this.intervalId);
}
render() {
return (
<Map center={this.props.center} zoom={this.props.zoom} places={this.state.markers} />
);
}
}
Demo

adding waypoints in the react-google-maps

editable samplecode how to use waypoints in the following code does the waypoints helps to plot the way which I updated in the database wheather the ponits will be based on the points I have updated
const DirectionsService = new google.maps.DirectionsService();
const DirectionsDisplay = new google.maps.DirectionsRenderer({suppressMarkers: true},{strokeColor:"#4a4a4a"});
DirectionsService.route({
origin: new google.maps.LatLng(this.state.orgin.latitude ,this.state.orgin.longitude),
destination: new google.maps.LatLng(this.state.destination.latitude ,this.state.destination.longitude),
travelMode: google.maps.TravelMode.DRIVING,
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result,
});
} else {
console.error(`error fetching directions ${result}`);
}
});
}).catch(function (err) {
});
}
})
)(props =>
<GoogleMap
defaultZoom={50}>
<DirectionsRenderer directions={props.directions} />
< Marker
position={{ lat:props.delivery!=undefined?props.delivery.latitude:null, lng: props.delivery!=undefined?props.delivery.longitude:null }} />
</GoogleMap>
);
return (
<MapWithADirectionsRenderer />
)
}
You can add waypoints by adding waypoints[] array of DirectionsWaypoint in your Directions Request.
You can check this documentation to learn more: https://developers.google.com/maps/documentation/javascript/directions#DirectionsRequests
Here's a sample waypoints array:
waypoints: [
{
location: new google.maps.LatLng(14.546748, 121.05455)
},
{
location: new google.maps.LatLng(14.552444,121.044488)
}
]
Here's a sample Direction Request with waypoints:
DirectionsService.route({
origin: new google.maps.LatLng(14.533593, 121.053128),
destination: new google.maps.LatLng(14.550895, 121.025079),
travelMode: google.maps.TravelMode.DRIVING,
waypoints: [
{
location: new google.maps.LatLng(14.546748, 121.05455)
},
{
location: new google.maps.LatLng(14.552444,121.044488)
}
]
}, (result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result,
});
} else {
console.error(`error fetching directions ${result}`);
}
});
A very simple way to implement wayPoints in React
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { withScriptjs } from "react-google-maps";
import Map from './components/Map';
function App() {
const MapLoader = withScriptjs(Map);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
</header>
<MapLoader
googleMapURL="https://maps.googleapis.com/maps/api/js?key=Key"
loadingElement={<div style={{ height: `100%` }} />}
/>
</div>
);
}
export default App;
And in your Map.js file
/*global google*/
import React, { Component } from "react";
import {
withGoogleMap,
withScriptjs,
GoogleMap,
DirectionsRenderer
} from "react-google-maps";
class Map extends Component {
state = {
directions: null,
};
componentDidMount() {
const directionsService = new google.maps.DirectionsService();
const origin = { lat: 6.5244, lng: 3.3792 };
const destination = { lat: 6.4667, lng: 3.4500};
directionsService.route(
{
origin: origin,
destination: destination,
travelMode: google.maps.TravelMode.DRIVING,
waypoints: [
{
location: new google.maps.LatLng(6.4698, 3.5852)
},
{
location: new google.maps.LatLng(6.6018,3.3515)
}
]
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
console.log(result)
this.setState({
directions: result
});
} else {
console.error(`error fetching directions ${result}`);
}
}
);
}
render() {
const GoogleMapExample = withGoogleMap(props => (
<GoogleMap
defaultCenter={{ lat: 6.5244, lng: 3.3792 }}
defaultZoom={13}
>
<DirectionsRenderer
directions={this.state.directions}
/>
</GoogleMap>
));
return (
<div>
<GoogleMapExample
containerElement={<div style={{ height: `500px`, width: "500px" }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
</div>
);
}
}
export default Map;
I believe this is okay

Resources