How to draw a custom triangle in renderItem in Apache Echarts - echarts

I want to draw custom triangle using renderItem method:
series: [
{
....
type: 'custom',
renderItem: renderGanttItem,
....
}
]
I see some build in functions how to draw rectangle in customer render item method. There are some good examples for it. But I could not find good examples how to draw some triangles. Maybe it is better to use some 'polygon'? Or maybe there are some other opportunities.
var rectNormal = clipRectByRect(params, {
x: x,
y: y,
width: barLength,
height: barHeight
});
return {
type: 'group',
children: [
{
type: 'rect',
ignore: !rectNormal,
shape: rectNormal,
style: api.style()
}
]
};
}
function clipRectByRect(params, rect) {
return echarts.graphic.clipRectByRect(rect, {
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height
});
}
How to draw a triangle?
I need something like this

Related

Changing leaflet markers to circleMarkers

I'm trying to Change Leaflet markers to circleMarker on data coming from geoJSON file.
Until now her is how I display data on map:
const geodesic = new L.Geodesic().addTo(map); /* Affiche les ligne géodésiques*/
geodesic.fromGeoJson(waypoints);
function pointFilter(feature) {
if (feature.geometry.type === "Point") return true
}
var points = new L.geoJson(waypoints, {filter: pointFilter}).addTo(map);
My geoJSON file contains LineStrings and Points.
The geodesic lines and standard icon markers are displayed, but I would change them by circleMarker between each lines.
Hope I'm clear enough.
Thanks
Pierre
This is what the code may look like, change it as you see fit.
Here'a a whole example - autocomplete-with-geojson
const geojsonlayer = L.geoJSON(object, {
style: function (feature) {
return {
color: feature.properties.color || "red",
weight: 7,
opacity: 1,
fillOpacity: 0.7,
};
},
pointToLayer: (feature, latlng) => {
if (feature.properties.type === "Point") {
return new L.circleMarker(latlng, {
radius: 20,
});
}
},
onEachFeature: function (feature, layer) {},
});

Color intersection leaflet circle

I have a question about leaflet/folium. I have multiple circles on a map.
All these circles does have some overlap with each other.
Wat I want to do is to draw a line/Polygon in a different color, where all circles meet (intersection).
This is an example of what I want to achieve. The blue line is the intersection and need a different color. Is this possible in folium or in pure leaflet? If so, how do I do that?
You can use the turfjs library and the turf.intersect method for this
/* eslint-disable no-undef */
/**
* intersection with turfjs
*/
// config map
let config = {
minZoom: 7,
maxZomm: 18,
};
// magnification with which the map will start
const zoom = 18;
// co-ordinates
const lat = 52.22977;
const lng = 21.01178;
// calling map
const map = L.map('map', config).setView([lat, lng], zoom);
// Used to load and display tile layers on the map
// Most tile servers require attribution, which you can set under `Layer`
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
}).addTo(map);
// three coordinate
const centers = [{
lat: 52.22990558765487,
lng: 21.01168513298035
},
{
lat: 52.22962958994604,
lng: 21.011593937873844
},
{
lat: 52.2297445891999,
lng: 21.012012362480167
}
]
// turf.circle option
const options = {
steps: 64,
units: 'meters',
options: {}
}
// circle radius
const radius = 30;
// array polygons
let polygons = [];
// set marker, add
centers.map(({
lat,
lng
}) => {
const polygon = turf.circle([lng, lat], radius, options);
// add cirkle polygon to map
L.geoJSON(polygon, {
color: "red",
weight: 2
}).addTo(map);
// add object to array
polygons.push(polygon);
})
// get intersection
const intersection = turf.intersect(...polygons);
// style intersection
const intersectionColor = {
color: "yellow",
weight: 2,
opacity: 1,
fillColor: "yellow",
fillOpacity: 0.7
};
// adding an intersection to the map
// and styling to this element
L.geoJSON(intersection, {
style: intersectionColor
}).addTo(map);
*,
:after,
:before {
box-sizing: border-box;
padding: 0;
margin: 0;
}
html {
height: 100%;
}
body,
html,
#map {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.6.0/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet#1.6.0/dist/leaflet.js"></script>
<script src="https://cdn.jsdelivr.net/npm/#turf/turf#5/turf.min.js"></script>
<div id="map"></div>

Cannot access GeoJSON layer in react-leaflet

I am using React Leaflet to render Leaflet map and its GeoJSON component to render polygons. I am trying to implement dragging multiple polygons together at once, as a group.
I added Leaflet.Path.Drag library and tried to reuse this code. I am able to get transformation matrix which is in parent's state. If I want to apply this matrix to multiple polygons with _transform method, it doesn't work. I think the reason is that matrix is not applied to correct layers, but I have no idea how to fix this.
codesandbox.io
App.js
import React from "react";
import { MapContainer, GeoJSON, TileLayer } from "react-leaflet";
import { geoJson, latLngBounds } from "leaflet";
import "./styles.css";
import "leaflet/dist/leaflet.css";
import { GeoJsonContainer } from "./GeoJsonContainer";
const objects = [
{
polygon: {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {},
geometry: {
type: "Polygon",
coordinates: [
[
[-104.98569488525392, 39.63431579014969],
[-104.98569488525392, 39.64165260123419],
[-104.97161865234376, 39.64165260123419],
[-104.97161865234376, 39.63431579014969]
]
]
}
}
]
}
},
{
polygon: {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {},
geometry: {
type: "Polygon",
coordinates: [
[
[-105.02964019775392, 39.6206315500488],
[-105.02964019775392, 39.65685252543906],
[-104.99067306518556, 39.65685252543906],
[-104.99067306518556, 39.6206315500488]
]
]
}
}
]
}
}
];
const getPolygonPointFromBounds = (latLngBounds) => {
const center = latLngBounds.getCenter();
const latlngs = [];
latlngs.push(latLngBounds.getSouthWest()); //bottom left
latlngs.push({ lat: latLngBounds.getSouth(), lng: center.lng }); //bottom center
latlngs.push(latLngBounds.getSouthEast()); //bottom right
latlngs.push({ lat: center.lat, lng: latLngBounds.getEast() }); // center right
latlngs.push(latLngBounds.getNorthEast()); //top right
latlngs.push({
lat: latLngBounds.getNorth(),
lng: latLngBounds.getCenter().lng
}); //top center
latlngs.push(latLngBounds.getNorthWest()); //top left
latlngs.push({
lat: latLngBounds.getCenter().lat,
lng: latLngBounds.getWest()
}); //center left
return latlngs;
};
export default function App() {
const [matrix, setMatrix] = React.useState(null);
let newBounds = [];
let selectBoundingBox = [];
objects.forEach((building) => {
const polygonBounds = geoJson(building.polygon).getBounds();
newBounds = [...newBounds, polygonBounds];
});
const polygonPoints = getPolygonPointFromBounds(latLngBounds(newBounds));
const convertedData = polygonPoints.map((point) => [point.lng, point.lat]);
convertedData.push([polygonPoints[0].lng, polygonPoints[0].lat]);
selectBoundingBox = convertedData;
let selectBoxData = null;
if (selectBoundingBox) {
selectBoxData = {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {},
geometry: {
type: "Polygon",
coordinates: [selectBoundingBox]
}
}
]
};
}
const handleFeature = (layer) => {
layer.makeDraggable();
layer.dragging.enable();
layer.on("drag", function (e) {
setMatrix(layer.dragging._matrix);
});
};
return (
<MapContainer center={[39.63563779557324, -104.99234676361085]} zoom={12}>
<TileLayer
attribution='&copy OpenStreetMap contributors'
url="https://{s}.tile.osm.org/{z}/{x}/{y}.png"
/>
{objects.map((object, i) => (
<GeoJsonContainer data={object} key={i} matrix={matrix} />
))}
<GeoJSON
data={selectBoxData}
style={() => ({
color: "green",
weight: 3,
opacity: 0.5
})}
draggable={true}
onEachFeature={(feature, layer) => handleFeature(layer)}
></GeoJSON>
</MapContainer>
);
}
GeoJsonContainer.js
import React from "react";
import { GeoJSON } from "react-leaflet";
require("leaflet-path-drag");
export const GeoJsonContainer = (props) => {
const geoJSONRef = React.useRef(null);
const layerRef = React.useRef(null);
React.useEffect(() => {
if (geoJSONRef?.current?._layers) {
console.log("mount layers", geoJSONRef.current?._layers);
}
}, []);
React.useEffect(() => {
if (geoJSONRef?.current._layers && props.matrix) {
console.log("transform layers", geoJSONRef.current._layers);
const key = Object.keys(geoJSONRef.current._layers)[0];
const geoJSONElementLayer = geoJSONRef.current._layers[key];
if (geoJSONElementLayer) {
console.log("geoJSONElementLayer", geoJSONElementLayer);
console.log("layerRef.current", layerRef.current);
geoJSONElementLayer._transform(props.matrix);
layerRef.current._transform(props.matrix);
}
}
}, [props.matrix]);
const handleFeature = (layer) => {
console.log("handleFeature layer", layer);
layerRef.current = layer;
layer.makeDraggable();
layer.dragging.enable();
};
return (
<GeoJSON
ref={geoJSONRef}
data={props.data.polygon}
style={() => ({
color: "#3388ff",
weight: 3,
opacity: 1
})}
dragging={true}
onEachFeature={(feature, layer) => handleFeature(layer)}
></GeoJSON>
);
};
Regarding
If I want to apply this matrix to multiple polygons with _transform
method, it doesn't work
This is the expected behavior in React since matrix prop needs to be immutable meaning a new array needs to be passed each time there is a change:
layer.on("drag", function (e) {
setMatrix([...layer.dragging._matrix]);
});
instead of:
layer.on("drag", function (e) {
setMatrix(layer.dragging._matrix);
});
This way GeoJsonContainer component should get re-rendered as expected.
Another matter concerns Leaflet.Path.Drag plugin, according to the referenced thread, in fact both drop and dropend events needs to be captured to properly apply transformation, so maybe instead of matrix prop, introduce a transform prop to keep matrix array and a flag to determine whether drop or is dropend event is triggered:
const handleFeature = (layer) => {
layer.makeDraggable();
layer.dragging.enable();
layer.on("drag", function (e) {
setTransform({matrix: layer.dragging._matrix, "end": false});
});
layer.on("dragend", function (e) {
setTransform({matrix: layer.dragging._matrix, "end": true});
});
};
and pass it into GeoJsonContainer component to apply geometry transform:
React.useEffect(() => {
if (props.transform) {
geoJSONRef.current.eachLayer((layer) => {
if (props.transform.end) dragDropTransform(layer);
else __dragTransform(layer);
});
}
}, [props.transform]);
where
function __dragTransform(layer) {
layer._transform(props.transform.matrix);
}
function dragDropTransform(layer) {
layer.dragging._transformPoints(props.transform.matrix);
layer._updatePath();
layer._project();
layer._transform(null);
}
Updated live demo
A possible improvement(s):
in the provided example two instances of JSON layers are instantiated, maybe to consider create a single instance of GeoJSON and apply a style per geometry?
Solution improvements proposal
In the provided example two layers are instantiated:
in App component
GeoJSON layer which renders a single outer geometry (polygon)
and in GeoJsonContainer component another one
GeoJSON layer which in turn renders a two inner geometries (polygons)
How about to merge both GeoJSON objects, something like this:
const dataSource = {...selectBoxData,...objects[0],...objects[1]}
and create a single layer instead:
<GeoJSON data={dataSource}></GeoJSON>
This way twice initialization for dragable layers could be avoided (refactoring of duplicated code)

Axes Range change dynamically

Does anybody know how can I change axis range in plotly's scatter3D via button click. I use relayout but it doesn't work
function updatet() {
Plotly.relayout(‘myDiv’, ‘zaxis.range’, [[0, 100]]);
}
Nothing happens (my range is still [0,1800])
For 3D plots zaxis.range needs to be inside scene in order to work.
Note: Your ticks look like some copy&paste/autoformat problem.
Plotly.d3.csv('https://raw.githubusercontent.com/plotly/datasets/master/3d-scatter.csv', function(err, rows) {
function unpack(rows, key) {
return rows.map(function(row) {
return row[key];
});
}
var trace1 = {
x: unpack(rows, 'x1'),
y: unpack(rows, 'y1'),
z: unpack(rows, 'z1'),
mode: 'markers',
marker: {
size: 12,
line: {
color: 'rgba(217, 217, 217, 0.14)',
width: 0.5
},
opacity: 0.8
},
type: 'scatter3d'
};
var trace2 = {
x: unpack(rows, 'x2'),
y: unpack(rows, 'y2'),
z: unpack(rows, 'z2'),
mode: 'markers',
marker: {
color: 'rgb(127, 127, 127)',
size: 12,
symbol: 'circle',
line: {
color: 'rgb(204, 204, 204)',
width: 1
},
opacity: 0.9
},
type: 'scatter3d'
};
var data = [trace1, trace2];
var layout = {
margin: {
l: 0,
r: 0,
b: 0,
t: 0,
}
};
Plotly.newPlot('myDiv', data, layout);
});
document.getElementById('updateButton').addEventListener('click', function() {
Plotly.relayout('myDiv', 'scene.zaxis.range', [0, 10 * Math.random()]);
});
<script src="https://cdn.plot.ly/plotly-latest.min.js">
</script>
<div id="myDiv" style="width:100%;height:100%"></div>
<button id='updateButton'>Change axis
</button>

Change onHover colour of TextField Material-UI v1

I m unable to change the onHover color of the TextField by overriding the classname. How can I do that?
I'm using material UI v1: https://github.com/callemall/material-ui/tree/v1-beta
Overriding with classes didn't help.
It worked by overriding MUIclass in createMuiTheme as below.
const theme = createMuiTheme({
overrides: {
MuiInput: {
underline: {
'&:hover:not($disabled):before': {
backgroundColor: 'rgba(0, 188, 212, 0.7)',
},
},
},
},
});
TextField is implemented using the Input component, which exposes a class named underline as part of its CSS API. Here is the the current definition of this class from the Input source:
underline: {
paddingBottom: 2,
'&:before': {
backgroundColor: theme.palette.input.bottomLine,
left: 0,
bottom: 0,
// Doing the other way around crash on IE11 "''" https://github.com/cssinjs/jss/issues/242
content: '""',
height: 1,
position: 'absolute',
right: 0,
transition: theme.transitions.create('backgroundColor', {
duration: theme.transitions.duration.shorter,
easing: theme.transitions.easing.ease,
}),
},
'&:hover:not($disabled):before': {
backgroundColor: theme.palette.text.primary,
height: 2,
},
'&$disabled:before': {
background: 'transparent',
backgroundImage: `linear-gradient(to right, ${theme.palette.input
.bottomLine} 33%, transparent 0%)`,
backgroundPosition: 'left top',
backgroundRepeat: 'repeat-x',
backgroundSize: '5px 1px',
},
},
To override the Input's classes, you need to pass them through the TextField using its InputProps property. Here is an example where I'm changing the color of the underline to green:
// define a class that will be used to modify the underline class
const styleSheet = createStyleSheet(theme => ({
greenUnderline: {
'&:before': {
backgroundColor: '#0f0',
},
},
}));
Override the Input's underline class via the TextField's InputProps:
<TextField
id="uncontrolled"
label="Uncontrolled"
defaultValue="foo"
className={classes.textField}
margin="normal"
InputProps={{ classes: { underline: classes.greenUnderline } }}
/>
This may not be exactly what you're looking to do, but it should get you started.
this worked for me:
export const theme = createMuiTheme({
overrides:{
MuiFilledInput:{
root:{
"&:hover": {
backgroundColor: '#5dc2a6',
}
}
}
});