I try to use OL5 vector tiles with global tile grid.
import 'ol/ol.css'
import { Map, View } from 'ol'
import MVT from 'ol/format/MVT'
import TileGrid from 'ol/tilegrid/TileGrid'
import VectorTileLayer from 'ol/layer/VectorTile'
import VectorTileSource from 'ol/source/VectorTile'
let zoom = 0
let center = [8531000, 5342500]
let resolutions = [
9.554628535647032,
4.777314267823516,
2.388657133911758,
1.19432856695588,
]
let extent = [0, 0, 20037508.342789244, 20037508.342789244]
const map = new Map({
target: 'map',
view: new View({
zoom: zoom,
center: center,
resolutions: resolutions,
})
})
const vectorTiles = new VectorTileLayer({
source: new VectorTileSource({
tileSize: 256,
projection: 'EPSG:3857',
format: new MVT(),
tileGrid: new TileGrid({
extent: extent,
resolutions: resolutions,
}),
url: 'http://localhost:8000/get-vector-tiles/{z}/{x}/{y}'
})
})
map.addLayer(vectorTiles)
A request looks like http://localhost:8000/get-vector-tiles/0/3487/6007,
as I understand {x}/{y} are coordinates(numbers) of a tile from origin(in my case 0,0).
The start resolution is 9.554628535647032,
therefore a tile size is 9.554628535647032 × 256 = 2445.984905126 meters
Estimating of requested area coordinates:
X: 2445.984905126 × 3487 = 8529149.3642
Y: 2445.984905126 × 6007 = 14693031.2943
Considering the center of map is [8531000, 5342500]:
X coordinate is right 8529149.3642 ~ 8531000,
while Y coordinate does not match 5342500 vs 14693031.2943
What's wrong?
Solved, the thing is the rendering starts at the top left corner, therefore 20037508.342789244 - 2445.984905126 × 6007 ~ 5342500, all right!
Related
I'm trying to link a grid of images to a scatterplot, so that the right scatterplot point is highlighted when hovering over the corresponding image.
Problem is that when registering DOM events iteratively, they seem to get overwritten, and as a result only the last point of the scatterplot gets highlighted. How can I register independent events for each image.
See reproducible examples below.
import io
import random
import ipywidgets as ipw
from ipyevents import Event
import numpy as np
import PIL
from PIL import Image as PILImage
from bqplot import LinearScale, Figure, Axis, Scatter
# Random ipywidget image generator
def random_image():
arr = np.random.randint(255, size=(200, 200, 3), dtype=np.uint8)
img = PILImage.fromarray(arr)
with io.BytesIO() as fileobj:
img.save(fileobj, 'PNG')
img_b = fileobj.getvalue()
img_w = ipw.Image(value=img_b)
return img_w
# Create data
data = [{'x': idx, 'y': random.randint(1,10), 'image': random_image()}
for idx in range(1,6)]
# Create scatter plot
xs = LinearScale()
ys = LinearScale()
xa = Axis(scale=xs)
ya = Axis(scale=ys, orientation='vertical')
points = Scatter(x=[d['x'] for d in data],
y=[d['y'] for d in data],
scales={'x': xs, 'y': ys})
highlighted_point = Scatter(x=[-1000], y=[-1000], # Dummy point out of view
scales={'x': xs, 'y': ys},
preserve_domain={'x': True, 'y': True},
colors=['red'])
fig = Figure(marks=[points, highlighted_point], axes=[xa,ya])
# Add DOM events to images
img_list = []
for d in data:
img = d['image']
event = Event(source=img, watched_events=['mouseenter'])
def handle_event(event):
highlighted_point.x = [d['x']]
highlighted_point.y = [d['y']]
event.on_dom_event(handle_event)
img_list.append(img)
images = ipw.HBox(img_list)
ipw.VBox([fig, images])
I found a way to do it using functools.partial
import functools
# Event handler
def handle_event(idx, event):
highlighted_point.x = [data[idx]['x']]
highlighted_point.y = [data[idx]['y']]
# Add DOM events to images
img_list = []
for idx, d in enumerate(data):
img = d['image']
event = Event(source=img, watched_events=['mouseenter'])
event.on_dom_event(functools.partial(handle_event, idx))
img_list.append(img)
images = ipw.HBox(img_list)
ipw.VBox([fig, images])
I would like to draw a rectangle based on a center point lat and lon assuming a given length and width, let's say 4.5m and 1.5m, respectively. I guess, we need the bearing too. I've made a simulation by drawing a rectangle on Google Earth, getting the positions and putting them on my code. However, I need something automatic. My question is how can I link the Cartesian coordinates to those four points (rectangle) in meters.
Here is my code:
import geopandas as gpd
from shapely.geometry import Polygon
lat_point_list = [41.404928, 41.404936, 41.404951, 41.404943]
lon_point_list = [2.177339, 2.177331, 2.177353, 2.177365]
polygon_geom = Polygon(zip(lon_point_list, lat_point_list))
import folium
m = folium.Map([41.4049364, 2.1773560], zoom_start=20)
folium.GeoJson(polygon_geom).add_to(m)
folium.LatLngPopup().add_to(m)
m
I would like this:
Update:
I know this is basic trigonometry. If I split the rectsngle into triangles, we can find the different points. I know it is basic for simple exercises, however, I don't know of it changes when using Cartesian coordinates. Then, my goal is to get the points A, B, C and D, knowing the center of the rectangle in latitude and longitude, length and width.
Get the rectangular (NE, SW) bounds of your point and use that as bounds to folium.Rectangle.
Example, using your data. 4.5m and 1.5m are a bit small to see the rectangle:
import geopy
import geopy.distance
import math
import folium
def get_rectangle_bounds(coordinates, width, length):
start = geopy.Point(coordinates)
hypotenuse = math.hypot(width/1000, length/1000)
# Edit used wrong formula to convert radians to degrees, use math builtin function
northeast_angle = 0 - math.degrees(math.atan(width/length))
southwest_angle = 180 - math.degrees(math.atan(width/length))
d = geopy.distance.distance(kilometers=hypotenuse/2)
northeast = d.destination(point=start, bearing=northeast_angle)
southwest = d.destination(point=start, bearing=southwest_angle)
bounds = []
for point in [northeast, southwest]:
coords = (point.latitude, point.longitude)
bounds.append(coords)
return bounds
# To get a rotated rectangle at a bearing, you need to get the points of the the recatangle at that bearing
def get_rotated_points(coordinates, bearing, width, length):
start = geopy.Point(coordinates)
width = width/1000
length = length/1000
rectlength = geopy.distance.distance(kilometers=length)
rectwidth = geopy.distance.distance(kilometers=width)
halfwidth = geopy.distance.distance(kilometers=width/2)
halflength = geopy.distance.distance(kilometers=length/2)
pointAB = halflength.destination(point=start, bearing=bearing)
pointA = halfwidth.destination(point=pointAB, bearing=0-bearing)
pointB = rectwidth.destination(point=pointA, bearing=180-bearing)
pointC = rectlength.destination(point=pointB, bearing=bearing-180)
pointD = rectwidth.destination(point=pointC, bearing=0-bearing)
points = []
for point in [pointA, pointB, pointC, pointD]:
coords = (point.latitude, point.longitude)
points.append(coords)
return points
start_coords = [41.4049364, 2.1773560]
length = 4.50 #in meters
width = 1.50
bearing = 45 #degrees
m = folium.Map(start_coords, zoom_start=20)
bounds = get_rectangle_bounds(tuple(start_coords),width, length )
points = get_rotated_points(tuple(start_coords), bearing, width, length)
folium.Rectangle(bounds=bounds,
fill=True,
color='orange',
tooltip='this is Rectangle'
).add_to(m)
# To draw a rotated rectangle, use folium.Polygon
folium.Polygon(points).add_to(m)
I have an SCNode which is dynamically created using the SCNPlane geometry to draw a plane using SceneKit.
How do I determine the normal vector to this plane?
Here is a playground demonstrating what I have tried. I have attached a screenshot of the resulting scene that is drawn. I don't think any of the cylinders, which represent the vectors obtained at the normal vector to this red plane.
//: Playground - noun: a place where people can play
import UIKit
import SceneKit
import PlaygroundSupport
// Set up the scene view
let frame = CGRect(
x: 0,
y: 0,
width: 500,
height: 300)
let sceneView = SCNView(frame: frame)
sceneView.showsStatistics = true
sceneView.autoenablesDefaultLighting = true
sceneView.allowsCameraControl = true
sceneView.scene = SCNScene()
// Setup our view into the scene
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.position = SCNVector3(x: 0, y: 0, z: 3)
sceneView.scene!.rootNode.addChildNode(cameraNode)
// Add a plane to the scene
let plane = SCNNode(geometry: SCNPlane(width: 3,height: 3))
plane.geometry?.firstMaterial!.diffuse.contents = UIColor.red.withAlphaComponent(0.5)
plane.geometry?.firstMaterial?.isDoubleSided = true
sceneView.scene!.rootNode.addChildNode(plane)
/*
normalSource = [SCNGeometrySource geometrySourceWithData:data
semantic:SCNGeometrySourceSemanticNormal
vectorCount:VERTEX_COUNT
floatComponents:YES
componentsPerVector:3 // nx, ny, nz
bytesPerComponent:sizeof(float)
dataOffset:offsetof(MyVertex, nx)
dataStride:sizeof(MyVertex)];
*/
let dataBuffer = plane.geometry?.sources(for: SCNGeometrySource.Semantic.normal)[0].data
let colorArray = [UIColor.red, UIColor.orange, UIColor.yellow, UIColor.green, UIColor.blue, UIColor.systemIndigo, UIColor.purple, UIColor.brown, UIColor.black, UIColor.systemPink]
let sceneGeometrySource = dataBuffer!.withUnsafeBytes {
(vertexBuffer: UnsafePointer<SCNVector3>) -> SCNGeometrySource in
let sceneVectors = Array(UnsafeBufferPointer(start: vertexBuffer, count: dataBuffer!.count/MemoryLayout<SCNVector3>.stride))
var i=0
for vector in sceneVectors{
let cyl = SCNCylinder(radius: 0.05, height: 3)
cyl.firstMaterial!.diffuse.contents = colorArray[i].withAlphaComponent(0.8)
let lineNode = SCNNode(geometry: cyl)
lineNode.eulerAngles = vector
sceneView.scene!.rootNode.addChildNode(lineNode)
}
return SCNGeometrySource(vertices: sceneVectors)
}
PlaygroundSupport.PlaygroundPage.current.liveView = sceneView
in the apple documentation https://developer.apple.com/documentation/accelerate/working_with_vectors in "Calculate the Normal of a Triangle" you can find the solution. You just need 3 points which are not on one line and then use this:
The following code defines the three vertices of the triangle:
let vertex1 = simd_float3(-1.5, 0.5, 0)
let vertex2 = simd_float3(1, 0, 3)
let vertex3 = simd_float3(0.5, -0.5, -1.5)
Your first step in calculating the normal of the triangle is to create two vectors defined by the difference between the vertices—representing two sides of the triangle:
let vector1 = vertex2 - vertex3
let vector2 = vertex2 - vertex1
The simd_cross function returns the vector that's perpendicular to the two vectors you pass it. In this example, the returned vector is the normal of the triangle. Because the normal represents a direction, you can normalize the value to get a unit vector:
let normal = simd_normalize(simd_cross(vector1, vector2))
I'm trying to draw car trips on a plane. I'm using Plotters library.
Here is some code example of trips' drawing procedure:
pub fn save_trips_as_a_pic<'a>(trips: &CarTrips, resolution: (u32, u32))
{
// Some initializing stuff
/// <...>
let root_area =
BitMapBackend::new("result.png", (resolution.0, resolution.1)).into_drawing_area();
root_area.fill(&WHITE).unwrap();
let root_area =
root_area.margin(10,10,10,10).titled("TITLE",
("sans-serif", 20).into_font()).unwrap();
let drawing_areas =
root_area.split_evenly((cells.1 as usize, cells.0 as usize));
for (index, trip) in trips.get_trips().iter().enumerate(){
let mut chart =
ChartBuilder::on(drawing_areas.get(index).unwrap())
.margin(5)
.set_all_label_area_size(50)
.build_ranged(50.0f32..54.0f32, 50.0f32..54.0f32).unwrap();
chart.configure_mesh().x_labels(20).y_labels(10)
.disable_mesh()
.x_label_formatter(&|v| format!("{:.1}", v))
.y_label_formatter(&|v| format!("{:.1}", v))
.draw().unwrap();
let coors = trip.get_points();
{
let draw_result =
chart.draw_series(series_from_coors(&coors, &BLACK)).unwrap();
draw_result.label(format!("TRIP {}",index + 1)).legend(
move |(x, y)|
PathElement::new(vec![(x, y), (x + 20, y)], &random_color));
}
{
// Here I put red dots to see exact nodes
chart.draw_series(points_series_from_trip(&coors, &RED));
}
chart.configure_series_labels().border_style(&BLACK).draw().unwrap();
}
}
What I got now on Rust Plotters:
So, after drawing it in the 'result.png' image file, I struggle to understand these "lines", because I don't see the map itself. I suppose, there is some way in this library to put a map "map.png" in the background of the plot. If I would use Python, this problem will be solved like this:
# here we got a map image;
img: Image.Image = Image.open("map-image.jpg")
img.putalpha(64)
imgplot = plt.imshow(img)
# let's pretend that we got our map size in pixels and coordinates
# just in right relation to each other.
scale = 1000
x_shift = 48.0
y_shift = 50.0
coor_a = Coordinate(49.1, 50.4)
coor_b = Coordinate(48.9, 51.0)
x_axis = [coor_a.x, coor_b.x]
x_axis = [(element-x_shift) * scale for element in x_axis]
y_axis = [coor_a.y, coor_b.y]
y_axis = [(element-y_shift) * scale for element in y_axis]
plt.plot(x_axis, y_axis, marker='o')
plt.show()
Desired result on Python
Well, that's easy on Python, but I got no idea, how to do similar thing on Rust.
I have two nvd3 graph on my site.
First graph with mouseover:
Code for first graph:
var chart = nv.models.multiBarChart()
.reduceXTicks(true) //If 'false', every single x-axis tick label will be rendered.
.rotateLabels(0) //Angle to rotate x-axis labels.
.showControls(true) //Allow user to switch between 'Grouped' and 'Stacked' mode.
.groupSpacing(0.1) //Distance between each group of bars.
;
d3.select('#chartSumme svg').datum([{ key: 'Row1', values:
[{ x:'2019', y: 328500},{ x:'2020', y: 8236000},{ x:'2021', y: 3162500},{ x:'2022', y: 2814500},{ x:'2023', y: 13377000},{ x:'2024', y: 17098500}]
}, { key: 'row2', values:
[{ x:'2019', y: 335550}...and so on]}
]).transition().duration(500).call(chart);
Second graph with mouseover:
Code for second graph:
var chart = nv.models.multiBarChart()
.reduceXTicks(true) //If 'false', every single x-axis tick label will be rendered.
.rotateLabels(0) //Angle to rotate x-axis labels.
.showControls(true) //Allow user to switch between 'Grouped' and 'Stacked' mode.
.groupSpacing(0.1) //Distance between each group of bars.
;
d3.select('#chart svg').datum([{ key: 'Row1', values:
[{ x:'2019', y: 0},{ x:'2020', y: 5000000},{ x:'2021', y: 0},{ x:'2022', y: 0},{ x:'2023', y: 0},{ x:'2024', y: 0}]
}, { key: 'row2', values:
[{ x:'2019', y: 2000}...and so on
},]).transition().duration(500).call(chart);
Question
As you can see no label is shown on the second graph with mouseover. First I thought there is an error in my code but then I found out that the label moved the position outside the graph:
Top is the first graph, then comes text (black box) and then the second graph with mouseover. The label from the second graph is shown on the first graph. How can I fix this that every label is shown in his graph?
Seems to be a bug, found this post and solution:
Line 742 add +window.scrollY
var positionTooltip = function() {
nv.dom.read(function() {
var pos = position(),
gravityOffset = calcGravityOffset(pos),
left = pos.left + gravityOffset.left,
top = pos.top + gravityOffset.top+window.scrollY;