How to fetch coordinates of Polygon in React.JS using react-google-maps - node.js

I wants to fetch all the coordinates of a polygon drawn on Google's Map. And here is my code
import React from "react";
import { compose, withProps } from "recompose";
import {
withScriptjs,
withGoogleMap,
GoogleMap,
Marker
} from "react-google-maps";
//import withScriptjs from "react-google-maps/lib/async/withScriptjs";
import { DrawingManager } from "react-google-maps/lib/components/drawing/DrawingManager";
const MyMapComponent = compose(
withProps({
/**
* Note: create and replace your own key in the Google console.
* https://console.developers.google.com/apis/dashboard
* The key "AIzaSyBkNaAGLEVq0YLQMi-PYEMabFeREadYe1Q" can be ONLY used in this sandbox (no forked).
*/
googleMapURL:
"https://maps.googleapis.com/maps/api/js?key=AIzaSyALpmb4KhFoR2Kcvty21gzzegprl4ilIgs&v=3.exp&libraries=geometry,drawing,places",
loadingElement: <div style={{ height: `100%` }} />,
containerElement: <div style={{ height: `400px` }} />,
mapElement: <div style={{ height: `100%` }} />
}),
withScriptjs,
withGoogleMap
)(props => (
<GoogleMap
defaultZoom={8}
defaultCenter={new window.google.maps.LatLng(-34.397, 150.644)}
>
<DrawingManager
defaultDrawingMode={
window.google.maps.drawing.OverlayType.ControlPosition
}
defaultOptions={{
drawingControl: true,
drawingControlOptions: {
position: window.google.maps.ControlPosition.TOP_CENTER,
drawingModes: [
window.google.maps.drawing.OverlayType.CIRCLE,
window.google.maps.drawing.OverlayType.POLYGON,
window.google.maps.drawing.OverlayType.POLYLINE,
window.google.maps.drawing.OverlayType.RECTANGLE
]
},
circleOptions: {
fillColor: `#ffff00`,
fillOpacity: 1,
strokeWeight: 5,
clickable: false,
editable: true,
zIndex: 1
}
}}
/>
{props.isMarkerShown && (
<Marker position={{ lat: -34.397, lng: 150.644 }} />
)}
</GoogleMap>
));
My focus of work is to fetch all the coordinates of that polygon that should be drawn on Google Maps.I also wants to store these coordinates in MongoDB using mongoose and NodeJs as backend.

We can use this function to get all the coordinates of a polygon or any other reactangle.
function getPaths(polygon) {
var polygonBounds = polygon.getPath();
var bounds = [];
for (var i = 0; i < polygonBounds.length; i++) {
var point = {
lat: polygonBounds.getAt(i).lat(),
lng: polygonBounds.getAt(i).lng()
};
bounds.push(point);
}
console.log(bounds);
}
And in GoogleMap component, i simplified the above code by given way.
<DrawingManager
drawingMode={"polygon"}
onPolygonComplete={value => console.log(getPaths(value))} />

Related

Tweening Paths With Svelte, D3, and Svelte Motion

I have a question regarding svelte animation and tweening paths. Here is a repl with the code https://svelte.dev/repl/b2509f1cfff54ee5856db44997f6fe53?version=3.53.1
I have
<script>
import { draw } from "svelte/transition";
import { tweened } from 'svelte/motion'
import { interpolateString, interpolateLab, interpolateTransformCss } from 'd3-interpolate';
import * as easings from 'svelte/easing';
import * as d3 from "d3";
import {path1} from './paths.js';
import {path2} from './paths.js';
import {path3} from './paths.js';
import {path4} from './paths.js';
let paths_1 = [
{id: "google", path: path1},
{id: "(direct)", path: path2}
]
let paths_2 = [
{id: "google", path: path3},
{id: "(direct)", path: path4}
]
let width = 800;
let height = 400;
let selected_path = paths_1;
function select(path) {
selected_path = path;
}
// animated copy of points
let animated_path = tweened(null, {
interpolate: interpolateString,
duration: 1000,
easing: easings.cubicOut
})
$: animated_path.set(selected_path);
</script>
<button on:click|preventDefault={() => select(paths_1)}>
Path1
</button>
<button on:click|preventDefault={() => select(paths_2)}>
Path2
</button>
<svg {width} {height}>
{#each $animated_path as {id, path}}
<path
stroke="black"
d={path}
/>
{/each}
</svg>
<style>
path {
fill: none;
}
</style>
But the paths are just disappearing and then the other ones are popping in again instead of making a nice transition.
I made it work with one path transitioning in this repl: https://svelte.dev/repl/560b12831bea443a84b861ce756b3a98?version=3.53.1
and looked up how to do the tweening from this one: https://svelte.dev/repl/b4c485ee69484fd8a63b8dc07c3b20a2?version=3.4.1

Typescript "error a constructor method accessor or property was expected" error

I'm new to typescript. In this problem I need to increase the counter of "setBigCar" or "setSmallCar" when the "carSmall" is false or the other way around then display it on my browser
const pageHeader = (
<PageHeader
style={{ backgroundColor: "#fff", marginTop: 4 }}
title="Car Management - Admin"
<span>{`Big Car: `}</span>, //not sure how to display it
<span>{`Small Car : `}</span>,
/>
);
function setTotalBoth { //error a constructor method accessor or property was expected
var setBigCar = 0;
var setSmallCar = 0;
if(carSmallCar == false) { setBigCar +1 }
else { setSmallCar +1}
};
Any advice is appreciated
const pageHeader = (
<PageHeader
style={{ backgroundColor: "#fff", marginTop: 4 }}
title="Car Management - Admin"
>
<span>{`Big Car: `}</span>, //not sure how to display it
<span>{`Small Car : `}</span>,
<PageHeader/>
);

Can't manipulate grouped objects in fabric.js after loading from JSON

I'm trying to implement load/save functionality in a fabric.js project (using fabric.js 5.2.1). The canvas reloads correctly, but I can no longer interact with objects in groups. Non-grouped objects are fine, but pretty much everything in my project is in a group.
<input type="button" id="loadJSON" value="Load JSON" />
<input type="button" id="colorize2" value="Background" />
<input type="button" id="colorize3" value="Square" />
<input type="button" id="colorize" value="Line" />
<canvas id="below" width="960" height="448" style="position:absolute; top:10; left:10;"></canvas>
var jsonString = '{"version":"5.2.1","objects":[{"type":"rect","version":"5.2.1","originX":"left","originY":"top","left":100,"top":100,"width":100,"height":100,"fill":"orange","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"id":"square","rx":0,"ry":0,"selectable":true,"perPixelTargetFind":false,"centeredScaling":false,"centeredRotation":true,"borderColor":"rgb(178,204,255)","cornerColor":"rgb(178,204,255)","cornerSize":13,"transparentCorners":true},{"type":"group","version":"5.2.1","originX":"left","originY":"top","left":477.7,"top":25.62,"width":5.25,"height":396.17,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":0,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"id":"ElementGroup","selectable":false,"perPixelTargetFind":false,"centeredScaling":false,"centeredRotation":true,"borderColor":"rgb(178,204,255)","cornerColor":"rgb(178,204,255)","cornerSize":13,"transparentCorners":true,"objects":[{"type":"path","version":"5.2.1","originX":"left","originY":"top","left":-2.63,"top":-198.09,"width":4.25,"height":395.17,"fill":"yellow","stroke":null,"strokeWidth":1,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"id":"centerLine01","selectable":false,"perPixelTargetFind":false,"centeredScaling":false,"centeredRotation":true,"borderColor":"rgb(178,204,255)","cornerColor":"rgb(178,204,255)","cornerSize":13,"transparentCorners":true,"path":[["M",478.19727,26.119141],["L",478.19727,421.29297],["L",482.45117,421.29297],["L",482.45117,26.119141],["L",478.19727,26.119141],["z"]]}]}],"background":"green","perPixelTargetFind":false,"centeredScaling":false,"centeredRotation":false,"backgroundColor":"blue"}'
var below = new fabric.Canvas('below', {
enableRetinaScaling: false,
preserveObjectStacking: true,
backgroundColor: 'green'
});
rect = new fabric.Rect({
top: 100,
left: 100,
width: 100,
height: 100,
fill: 'blue',
id: 'square'
})
centerLine01 = new fabric.Path("M 478.19727 26.119141 L 478.19727 421.29297 L 482.45117 421.29297 L 482.45117 26.119141 L 478.19727 26.119141 z", {
fill: 'red',
id: 'centerLine01',
visible: true,
selectable: false,
evented: false
});
ElementGroup = new fabric.Group([centerLine01], {
id: 'ElementGroup',
visible: true,
selectable: false,
evented: false
})
below.add(rect)
below.add(ElementGroup)
below.renderAll()
$("#loadJSON").on("click", function(e) {
below.loadFromJSON(jsonString, below.renderAll.bind(below), function(o, object) {
below.add(object)
});
below.renderAll.bind(below)
console.log("JSON loaded!")
})
$("#colorize").on("click", function(e) {
const randomColor = "#" + Math.floor(Math.random() * 16777215).toString(16)
ElementGroup.getObjects().forEach(function(o) {
if (o.id == "centerLine01") {
o.set("fill", randomColor)
below.renderAll()
}
})
})
$("#colorize2").on("click", function(e) {
const randomColor = "#" + Math.floor(Math.random() * 16777215).toString(16)
below.setBackgroundColor(randomColor, below.renderAll.bind(below))
})
$("#colorize3").on("click", function(e) {
const randomColor = "#" + Math.floor(Math.random() * 16777215).toString(16)
below.getObjects().forEach(function(o) {
if (o.id == "square") {
o.set("fill", randomColor)
below.renderAll()
}
})
})
In this sample, the Background, Square and Line buttons all work out of the gate, but as soon as you reload the canvas from JSON the Line button no longer does anything, even though the object is still getting found (a simple console.log() verifies that). Not quite sure what the issue is, though.
jsfiddle here: https://jsfiddle.net/eriqjaffe/jo25d8qs/8/

What's the real meaning of randomize=false using cose-bilkent layout

What the real meaning of randomize=false using cose-bilkent layout ?
I can only read such a simple comment to describe this feature in this extension wiki page.
// Whether to enable incremental mode
randomize: true,
I also find another description for randomize in cytoscape.js-expand-collapse wiki page.
// recommended usage: use cose-bilkent layout with randomize: false to preserve mental map upon expand/collapse
So I have learned that randomize: false is to preserve mental map. But what is the real behavior ?
Suppose that I have run a topology graph using cose-bilkent layout, then call the following snippet to append new nodes and edges to the graph with randomize: false. What will be the expected result on incremental mode ? Should the old nodes and edges keep the relative position as so called mental map , while new nodes and edges should be positioned to a resonable coordinate?
var demoId = 10000;
document.getElementById("append").addEventListener("click", function() {
var tid = cy.nodes()[Math.floor(Math.random()*cy.nodes().length)].data().id;
var sid = ++demoId;
var data = {
nodes : [
{data:{"id":"id"+source, "name":"name"+source}}
],
edges :[
{data:{"id":"id"+source+"-"+target, "source":"id"+source, "target":target}}
]
};
cy.add(data);
cy.layout({
name: 'cose-bilkent',
animate: false,
randomize : false,
fit : false
}).run();
});
See my demo. My test result is that all nodes and edges will be relayout. You could watch some nodes and their neighborhood. The neighborhood will be changed after each layout. I can't get a clear mental map. So could the experts explain how incremental layout task effect here.
document.addEventListener("DOMContentLoaded", function() {
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
ready: function() {
this.layout({
name: "cose-bilkent",
animationDuration: 1000
}).run();
},
style: [{
selector: "node",
style: {
'content': 'data(id)',
'text-valign': 'center',
'text-halign': 'center',
'background-color': 'blue',
'color':'red',
'width':'10px',
'height':'10px'
}
},
{
selector: ":parent",
style: {
"background-opacity": 0.333
}
},
{
selector: "edge",
style: {
width: 3,
"line-color": "#ad1a66"
}
}
],
elements: [{"group":"nodes","data":{"id":"p_1","parent":"n_72"}},{"group":"nodes","data":{"id":"p_298","parent":"n_72"}},{"group":"nodes","data":{"id":"p_4","parent":"n_72"}},{"group":"nodes","data":{"id":"p_5","parent":"n_72"}},{"group":"nodes","data":{"id":"p_9","parent":"n_72"}},{"group":"nodes","data":{"id":"p_32","parent":"n_72"}},{"group":"nodes","data":{"id":"p_607","parent":"n_72"}},{"group":"nodes","data":{"id":"p_57","parent":"n_72"}},{"group":"nodes","data":{"id":"p_242","parent":"n_72"}},{"group":"nodes","data":{"id":"p_64","parent":"n_72"}},{"group":"nodes","data":{"id":"p_77","parent":"n_72"}},{"group":"nodes","data":{"id":"p_81","parent":"n_72"}},{"group":"nodes","data":{"id":"p_82","parent":"n_72"}},{"group":"nodes","data":{"id":"p_289","parent":"n_72"}},{"group":"nodes","data":{"id":"p_803","parent":"n_72"}},{"group":"nodes","data":{"id":"n_0"}},{"group":"nodes","data":{"id":"n_72"}},{"group":"edges","data":{"source":"n_0","target":"n_0","id":"n_0-n_0"}},{"group":"edges","data":{"source":"n_0","target":"p_81","id":"n_0-p_81"}},{"group":"edges","data":{"source":"n_0","target":"p_4","id":"n_0-p_4"}},{"group":"edges","data":{"source":"n_0","target":"p_298","id":"n_0-p_298"}},{"group":"edges","data":{"source":"n_0","target":"p_803","id":"n_0-p_803"}},{"group":"edges","data":{"source":"n_0","target":"p_607","id":"n_0-p_607"}},{"group":"edges","data":{"source":"n_0","target":"p_1","id":"n_0-p_1"}},{"group":"edges","data":{"source":"n_0","target":"p_289","id":"n_0-p_289"}},{"group":"edges","data":{"source":"n_0","target":"p_57","id":"n_0-p_57"}},{"group":"edges","data":{"source":"n_0","target":"p_82","id":"n_0-p_82"}},{"group":"edges","data":{"source":"n_0","target":"p_32","id":"n_0-p_32"}},{"group":"edges","data":{"source":"p_77","target":"n_0","id":"p_77-n_0"}},{"group":"edges","data":{"source":"p_64","target":"n_0","id":"p_64-n_0"}},{"group":"edges","data":{"source":"p_607","target":"n_0","id":"p_607-n_0"}},{"group":"edges","data":{"source":"p_5","target":"n_0","id":"p_5-n_0"}},{"group":"edges","data":{"source":"p_9","target":"n_0","id":"p_9-n_0"}},{"group":"edges","data":{"source":"p_9","target":"p_803","id":"p_9-p_803"}},{"group":"edges","data":{"source":"p_242","target":"n_0","id":"p_242-n_0"}}],
}));
var demoId = 10000;
document.getElementById("append").addEventListener("click", function() {
var tid = cy.nodes()[Math.floor(Math.random()*cy.nodes().length)].data().id;
var sid = ++demoId;
var data = {
nodes : [
{data:{"id":sid}}
],
edges :[
{data:{"id":sid+"-"+tid, "source":sid, "target":tid}}
]
};
cy.add(data);
cy.layout({
name: 'cose-bilkent',
animate: false,
randomize : false,
fit : true
}).run();
});
});
body {
font-family: helvetica;
font-size: 14px;
}
#cy {
height: 100%;
width: 90%;
position: absolute;
}
h1 {
opacity: 0.5;
font-size: 1em;
}
button {
margin-right: 10px;
}
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<!--polyfills are needed for this extension for old browsers like IE -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/core-js/2.5.7/shim.min.js"></script>
<script src="https://unpkg.com/layout-base/layout-base.js"></script>
<script src="https://unpkg.com/cose-base/cose-base.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-cose-bilkent#4.1.0/cytoscape-cose-bilkent.min.js"></script>
<body>
<button id="append" class="button">Append</button>
<div id="cy"></div>
</body>

Displaying a list of Markers with React

im trying to display a list of Markers with lat and lng stored on my mongodb database.
Im using the google-maps-react and this is my child map component
import {
Map,
InfoWindow,
Marker,
GoogleApiWrapper,
Polygon
} from 'google-maps-react';
import React, { Component } from 'react';
export class MapContainer extends Component {
render() {
const coords = this.props.initialCenter;
const position = this.props.position;
const paths = this.props.paths;
const style = this.props.style;
const center = this.props.center;
console.log(this.props);
return (
<Map
google={this.props.google}
zoom={18}
initialCenter={coords}
style={style}
center={center}
>
<Marker
onClick={this.onMarkerClick}
name={'Current location'}
position={position}
/>
<InfoWindow onClose={this.onInfoWindowClose}>
<div>
<h1>Test</h1>
</div>
</InfoWindow>
<Polygon
paths={paths}
strokeColor='#0000FF'
strokeOpacity={0.8}
strokeWeight={2}
fillColor='#0000FF'
fillOpacity={0.35}
/>
</Map>
);
}
}
export default GoogleApiWrapper({
apiKey: process.env.REACT_APP_GOOGLE_MAPS_API_TEST
})(MapContainer);
I get the coordinates from my endpoint here
const centers = this.props.places.places.map(place => {
return {
lat: place.center[0],
lng: place.center[1]
};
});
And im trying to display the list like this:
const position = { ...centers} ->does nothing
//const position = { lat: 41.53113384600326, lng: -8.619018495082855 }; -> prints one Marker
//const position = centers[0] -> prints one Marker
Im using that child component like this:
<MapContainer
initialCenter={initialCenter}
position={position}
paths={paths}
style={style}
center={center}
/>
Any help on this ?
EDIT:
Changed child component to
<Marker
onClick={this.onMarkerClick}
name={'Current location'}
position={{ position }}
/>
And on parent:
Where centers is:
<MapContainer
initialCenter={initialCenter}
paths={paths}
style={style}
center={center}
position={centers.map(p => (
<Marker
onClick={() => this.onMarkerClick(p)}
name={'Current location'} // You should probably have a "name" field for each positions
position={p}
/>
))}
></MapContainer>
const centers = this.props.places.places.map(place => {
return {
lat: place.center[0],
lng: place.center[1]
};
});
Still not showing
You need to send the array of position as props to your component, and then render them all like this:
this.props.positions.map(p => <Marker
onClick={() => this.onMarkerClick(p)}
name={'Current location'} // You should probably have a "name" field for each positions
position={p}
/>)

Resources