I am creating a map with markers with the leaflet library.
var mymap = L.map('mapid', {
center: [${mapCenter.lat}, ${mapCenter.lng}],
zoom: 9
});
I am using this algorithm to calculate the center of the map based in all the markers I have to show
double minLatitude = Double.MAX_VALUE;
double maxLatitude = Double.MIN_VALUE;
double minLongitude = Double.MAX_VALUE;
double maxLongitude = Double.MIN_VALUE;
for (ViewMarker marker : markers) {
if (marker.getLng() > maxLongitude) {
maxLongitude = marker.getLng();
}
if (marker.getLng() < minLongitude) {
minLongitude = marker.getLng();
}
if (marker.getLat() > maxLatitude) {
maxLatitude = marker.getLat();
}
if (marker.getLat() < minLatitude) {
minLatitude = marker.getLat();
}
}
double centerLatitude = (minLatitude + maxLatitude ) / 2;
double centerLongitude = (minLongitude + maxLongitude) / 2;
But as you see in the image, I wouldn't say that the map is in the center
Instantiate a L.LatLngBounds, insert the markers' LatLngs there, and then do a map.fitBounds().
Alternatively, use a FeatureGroup to hold all the markers inside, as it provides a getBounds().
Unless you know the basics of geodesy and map projections, do not use naïve solutions like centerlat = (lat1 + lat2) / 2, which is not true for the default Mercator projection.
Related
I am trying to use a CIColorKernel or CIBlendKernel with sampler arguments but the program crashes. Here is my shader code which compiles successfully.
extern "C" float4 wipeLinear(coreimage::sampler t1, coreimage::sampler t2, float time) {
float2 coord1 = t1.coord();
float2 coord2 = t2.coord();
float4 innerRect = t2.extent();
float minX = innerRect.x + time*innerRect.z;
float minY = innerRect.y + time*innerRect.w;
float cropWidth = (1 - time) * innerRect.w;
float cropHeight = (1 - time) * innerRect.z;
float4 s1 = t1.sample(coord1);
float4 s2 = t2.sample(coord2);
if ( coord1.x > minX && coord1.x < minX + cropWidth && coord1.y > minY && coord1.y <= minY + cropHeight) {
return s1;
} else {
return s2;
}
}
And it crashes on initialization.
class CIWipeRenderer: CIFilter {
var backgroundImage:CIImage?
var foregroundImage:CIImage?
var inputTime: Float = 0.0
static var kernel:CIColorKernel = { () -> CIColorKernel in
let url = Bundle.main.url(forResource: "AppCIKernels", withExtension: "ci.metallib")!
let data = try! Data(contentsOf: url)
return try! CIColorKernel(functionName: "wipeLinear", fromMetalLibraryData: data) //Crashes here!!!!
}()
override var outputImage: CIImage? {
guard let backgroundImage = backgroundImage else {
return nil
}
guard let foregroundImage = foregroundImage else {
return nil
}
return CIWipeRenderer.kernel.apply(extent: backgroundImage.extent, arguments: [backgroundImage, foregroundImage, inputTime])
}
}
It crashes in the try line with the following error:
Fatal error: 'try!' expression unexpectedly raised an error: Foundation._GenericObjCError.nilError
If I replace the kernel code with the following, it works like a charm:
extern "C" float4 wipeLinear(coreimage::sample_t s1, coreimage::sample_t s2, float time)
{
return mix(s1, s2, time);
}
So there are no obvious errors in the code, such as passing incorrect function name or so.
For your use case, you actually can use a CIColorKernel. You just have to pass the extent of your render destination to the kernel as well, then you don't need the sampler to access it.
The kernel would look like this:
extern "C" float4 wipeLinear(coreimage::sample_t t1, coreimage::sample_t t2, float4 destinationExtent, float time, coreimage::destination destination) {
float minX = destinationExtent.x + time * destinationExtent.z;
float minY = destinationExtent.y + time * destinationExtent.w;
float cropWidth = (1.0 - time) * destinationExtent.w;
float cropHeight = (1.0 - time) * destinationExtent.z;
float2 destCoord = destination.coord();
if ( destCoord.x > minX && destCoord.x < minX + cropWidth && destCoord.y > minY && destCoord.y <= minY + cropHeight) {
return t1;
} else {
return t2;
}
}
And you call it like this:
let destinationExtent = CIVector(cgRect: backgroundImage.extent)
return CIWipeRenderer.kernel.apply(extent: backgroundImage.extent, arguments: [backgroundImage, foregroundImage, destinationExtent, inputTime])
Note that the last destination parameter in the kernel is passed automatically by Core Image. You don't need to pass it with the arguments.
Yes, you can't use samplers in CIColorKernel or CIBlendKernel. Those kernels are optimized for the use case where you have a 1:1 mapping from input pixel to output pixel. This allows Core Image to execute multiple of these kernels in one command buffer since they don't require any intermediate buffer writes.
A sampler would allow you to sample the input at arbitrary coordinates, which is not allowed in this case.
You can simply use a CIKernel instead. It's meant to be used when you need to sample the input more freely.
To initialize the kernel, you need to adapt the code like this:
static var kernel: CIKernel = {
let url = Bundle.main.url(forResource: "AppCIKernels", withExtension: "ci.metallib")!
let data = try! Data(contentsOf: URL)
return try! CIKernel(functionName: "wipeLinear", fromMetalLibraryData: data)
}()
When calling the kernel, you now need to also provide a ROI callback, like this:
let roiCallback: CIKernelROICallback = { index, rect -> CGRect in
return rect // you need the same region from the input as the output
}
// or even shorter
let roiCallback: CIKernelROICallback = { $1 }
return CIWipeRenderer.kernel.apply(extent: backgroundImage.extent, roiCallback: roiCallback, arguments: [backgroundImage, foregroundImage, inputTime])
Bonus answer:
For this blending effect, you actually don't need any kernel at all. You can achieve all that with simple cropping and compositing:
class CIWipeRenderer: CIFilter {
var backgroundImage:CIImage?
var foregroundImage:CIImage?
var inputTime: CGFloat = 0.0
override var outputImage: CIImage? {
guard let backgroundImage = backgroundImage else { return nil }
guard let foregroundImage = foregroundImage else { return nil }
// crop the foreground based on time
var foregroundCrop = foregroundImage.extent
foregroundCrop.size.width *= inputTime
foregroundCrop.size.height *= inputTime
return foregroundImage.cropped(to: foregroundCrop).composited(over: backgroundImage)
}
}
I have a NSManagedObject derived class (entity) whose instances are persisted in a local SQL-lite storage. The class has also longitude and latitude properties and I need to fetch the entities based on the distance from specific coordinates. I tried to used NSPredicate with custom functions, but I couldn't find documentation on how to implement the function (... if it does support that). Has anyone an idea on how to perform that kind of dynamic filtering on Core Data entities ? I tried with NSPredicate withBlock, but it doesn't work on objects persisted on SQL-Lite databases. Please help.
You can't do that with Core Data. You could use a Transient property to model the distance from your dynamic location, after the entities have been fetched, and even order the items based on that Transient property. But you can only fetch properties from the persistent store if they are persistent properties.
In practice, I've found it's very fast to query points in a rectangular window, if the coordinates are indexed. Build the window by taking the dynamic position's latitude/longitude, plus/minus the search radius, throwing in a cosine(latitude) adjustment for the longitude window if it's unprojected data (pure lat/lon, not UTM or similar grid).
If that's really not fast enough, you could store a geohash on your entities, which gives you a string you can do a prefix search on. See https://en.wikipedia.org/wiki/Geohash for a discussion. Or you could implement a true spatial index. But I've never had a Core Data performance problem that made it worth implementing either of those approaches.
I store my locations in the data model as latitude/longitude coordinates. Then I wrote some helper extensions to find a semirectangular region of lat/lon coordinates and query by that. This greatly limits the result set, so if you need to sort by location you can sort the resulting objects using the CLLocation distance calculator.
I create a CLCircularRegion and you can see my buildPredicate() function that creates the query.
Here's the code I'm using:
Swift 3
extension CLLocationDegrees {
static var north: CLLocationDegrees {
return 90.0
}
static var south: CLLocationDegrees {
return -90.0
}
static var east: CLLocationDegrees {
return 180.0
}
static var west: CLLocationDegrees {
return -180.0
}
var radians: Double {
return Double.pi * self / 180.0
}
}
extension CLLocationCoordinate2D {
var metersPerDegreeLatitude: CLLocationDistance {
return 111319.4907932736
}
var metersPerDegreeLongitude: CLLocationDistance {
return max(0.0, cos(self.latitude.radians) * self.metersPerDegreeLatitude)
}
}
extension CLCircularRegion {
var northernmostLatitude: CLLocationDegrees {
let longitude = self.center.latitude + self.radius / self.center.metersPerDegreeLatitude
return min(longitude, .north)
}
var southernmostLatitude: CLLocationDegrees {
let longitude = self.center.latitude - self.radius / self.center.metersPerDegreeLatitude
return max(longitude, .south)
}
var easternmostLongitude: CLLocationDegrees {
guard self.northernmostLatitude <= .north else {
return .east
}
guard self.southernmostLatitude >= .south else {
return .east
}
return min(.east, self.center.longitude + self.radius / (self.center.metersPerDegreeLongitude + 0.0001))
}
var westernmostLongitude: CLLocationDegrees {
guard self.northernmostLatitude <= .north else {
return .west
}
guard self.southernmostLatitude >= .south else {
return .west
}
return max(.west, self.center.longitude - self.radius / (self.center.metersPerDegreeLongitude + 0.0001))
}
func buildPredicate(latitudeName: String = "latitude", longitudeName: String = "longitude") -> NSPredicate {
let args = [self.southernmostLatitude, self.northernmostLatitude, self.westernmostLongitude, self.easternmostLongitude]
return NSPredicate(format: "\(latitudeName) >= %# && \(latitudeName) <= %# && \(longitudeName) >= %# && \(longitudeName) <= %#", argumentArray: args)
}
}
Here is the answer https://www.objc.io/issues/4-core-data/core-data-fetch-requests/
static double const D = 80. * 1.1;
double const R = 6371009.; // Earth readius in meters
double meanLatitidue = pointOfInterest.latitude * M_PI / 180.;
double deltaLatitude = D / R * 180. / M_PI;
double deltaLongitude = D / (R * cos(meanLatitidue)) * 180. / M_PI;
double minLatitude = pointOfInterest.latitude - deltaLatitude;
double maxLatitude = pointOfInterest.latitude + deltaLatitude;
double minLongitude = pointOfInterest.longitude - deltaLongitude;
double maxLongitude = pointOfInterest.longitude + deltaLongitude;
request.result = [NSPredicate predicateWithFormat:
#"(%# <= longitude) AND (longitude <= %#)"
#"AND (%# <= latitude) AND (latitude <= %#)",
#(minLongitude), #(maxLongitude), #(minLatitude), #(maxLatitude)];
Here's the Swift version of above answer
let D: Double = 80 * 1.1
let R: Double = 6371009
let meanLatitidue = pointOfInterest.coordinate.latitude * .pi / 180
let deltaLatitude = D / R * 180 / .pi
let deltaLongitude = D / (R * cos(meanLatitidue)) * 180 / .pi
let minLatitude: Double = pointOfInterest.coordinate.latitude - deltaLatitude
let maxLatitude: Double = pointOfInterest.coordinate.latitude + deltaLatitude
let minLongitude: Double = pointOfInterest.coordinate.longitude - deltaLongitude
let maxLongitude: Double = pointOfInterest.coordinate.longitude + deltaLongitude
let predicate = NSPredicate(format: "(%lf <= longitude) AND (longitude <= %lf) AND (%lf <= latitude) AND (latitude <= %lf)", minLongitude, maxLongitude,minLatitude, maxLatitude)
I am trying to access an array in a function, as far as I understand there is no Arraylists in Swift, so I am trying to use regular list:
func findNearsetPoints(pointsArray: [Point] , myPoint: Point )-> Array <Point>{
var twoPoint = [Point]()
var minDist1:Double = DBL_MAX;
var minDist2:Double = DBL_MAX;
var distance:Double = 0
for element in pointsArray{
distance = getDistanceBetweenTwoPoints(pointsArray[element], Point); //error 0
if (distance < minDist1) {
minDist1 = distance;
twoPoint[1] = twoPoint[0];
twoPoint[0] = pointsArray[element]; // error 1
}
else if (distance < minDist2) {
minDist2 = distance;
twoPoint[1] = pointsArray[element]; //error 1
}
}
return twoPoint;
}
func getDistanceBetweenTwoPoints(point1:Point , point2:Point )->Double{
return 5; //TODO
}
error 0:
/Users/user/Desktop/proj/ViewController.swift:145:38: Cannot subscript a value of type '[ViewController.Point]' with an index of type 'ViewController.Point'
error 1:
/Users/user/Desktop/proj/ViewController.swift:149:38: Cannot subscript a value of type '[ViewController.Point]' with an index of type 'ViewController.Point'
What is wrong with the code?
thanks!
your element is already a Point and not an index.
for element in pointsArray{
distance = getDistanceBetweenTwoPoints(point1: element, point2: myPoint) // fix here
if (distance < minDist1) {
minDist1 = distance;
twoPoint[1] = twoPoint[0];
twoPoint[0] = element; // fix here
}
else if (distance < minDist2) {
minDist2 = distance;
twoPoint[1] = element; // fix here
}
}
PS:
take a loook also to this question "Sort array by calculated distance in Swift" for better calculation of distance. just sort the array by distance and then take the first from the array after it is sorted. thats more easy to do
You declared getDistanceBetweenTwoPoints as taking named parameters.
With the current declaration, you need to call it using this syntax:
getDistanceBetweenTwoPoints(point1: aPoint, point2: anotherPoint)
If you want to call it without labels on the parameters then you should redefine it like this:
func getDistanceBetweenTwoPoints(
_ point1:Point ,
_ point2:Point )->Double{
return 5; //TODO
}
Suppose I have three cells, one containing the distance, one the speed and the other the time. Now I want to be able to fill in two cells (e.g. distance and speed) and have the time calculated in the third cell. But when I fill in the speed and the time, the distance should be calculated.
How can this be achieved in google spreadsheets? I suppose there should be some kind of check to not create an infinite loop. I tried to search for cell synchronization or cell dependency, but I think this is not the right term for this problem.
You can do this with a nested if statement:
=if(And(isnumber(A2),isnumber(C2)),ROUND(A2/C2),if(And(isnumber(A2),isnumber(B2)),round(A2/B2),if(and(isnumber(B2),isnumber(C2)),B2*TIMEVALUE(C2)*24,)))
Here is a screenshot for all three scenarios:
You may use OnEdit event:
user enters some value (speed, row, distance)
script checks for other values,
calculates the result
paste the result into cells
The whole text of script is here:
function onEdit(t) {
var workSheetName = 'Sheet1'; // change sheet name to yours
var distanceRangeAddress = 'A2:A'; // change if needed
var speedRangeAddress = 'B2:B'; // change if needed
var timeRangeAddress = 'C2:C'; // change if needed
var ss = SpreadsheetApp.getActiveSheet();
var sn = ss.getName();
var distance, speed, time;
if (sn == workSheetName) {
var tr = t.range;
if (t.oldValue != tr.getValue() && tr.getNumColumns() == 1 && tr.getNumRows() == 1) {
// distance
var myr = ss.getRange(distanceRangeAddress);
if (RangeIntersect(tr, myr)) {
distance = tr.getValue();
speed = tr.offset(0, 1).getValue();
time = tr.offset(0, 2).getValue();
if (time > 0) {
speed = distance / time;
tr.offset(0, 1).setValue(time);
return;
}
if (speed > 0) {
time = distance / speed;
tr.offset(0, 2).setValue(time);
return;
}
}
// speed
myr = ss.getRange(speedRangeAddress);
if (RangeIntersect(tr, myr)) {
speed = tr.getValue();
distance = tr.offset(0, -1).getValue();
time = tr.offset(0, 1).getValue();
if (time > 0) {
distance = speed * time;
tr.offset(0, -1).setValue(distance);
return;
}
if (distance > 0) {
time = distance / speed;
tr.offset(0, 1).setValue(time);
return;
}
}
// time
myr = ss.getRange(timeRangeAddress);
if (RangeIntersect(tr, myr)) {
time = tr.getValue();
distance = tr.offset(0, -2).getValue();
speed = tr.offset(0, -1).getValue();
if (speed > 0) {
distance = speed * time;
tr.offset(0, -2).setValue(distance);
return;
}
if (distance > 0) {
speed = distance / time;
tr.offset(0, -1).setValue(speed);
return;
}
}
}
}
}
function RangeIntersect(R1, R2) {
var LR1 = R1.getLastRow();
var Ro2 = R2.getRow();
if (LR1 < Ro2) return false;
var LR2 = R2.getLastRow();
var Ro1 = R1.getRow();
if (LR2 < Ro1) return false;
var LC1 = R1.getLastColumn();
var C2 = R2.getColumn();
if (LC1 < C2) return false;
var LC2 = R2.getLastColumn();
var C1 = R1.getColumn();
if (LC2 < C1) return false;
return true;
}
You may want to change parametres in the beginning of the script:
var workSheetName = 'Sheet1'; // change sheet name to yours
var distanceRangeAddress = 'A2:A'; // change if needed
var speedRangeAddress = 'B2:B'; // change if needed
var timeRangeAddress = 'C2:C'; // change if needed
Here's example file.
The script could be improved and adopted to your needs. Please, look for more information here.
I am trying to animate a line based on the given coordinates array comprising of latitude and longitude and I want to call my function just once and my coordinates name is: replayData.
map.on('postcompose', function (event) {
var vectorContext = event.vectorContext;
var frameState = event.frameState;
vectorContext.setFillStrokeStyle(null, animatedLineStroke);
var features = lineVectorSource.getFeatures();
for (var k = 0; k < features.length; k++) {
var feature = features[k];
if (!feature.get('finished')) {
var coords = feature.getGeometry().getCoordinates();
var elapsedTime = frameState.time - feature.get('start');
var elapsedPoints = elapsedTime * pointsPerMs;
if (elapsedPoints >= coords.length) {
feature.set('finished', true);
}
var maxIndex = Math.min(elapsedPoints, coords.length);
var currentLine = new ol.geom.LineString(coords.slice(0, maxIndex));
if (feature.get('iValue') == undefined || feature.get('iValue') < k) {
// drawMovingCarMarker(coords, k);
feature.set('iValue', k)
}
vectorContext.drawLineStringGeometry(currentLine, feature);
}
}
frameState.animate = true;
});
What this function is doing is first collecting all the values from for loop and then starting the animation. And because of this if I've 5 points the line between first two points will be drawn 5 times then 4,3, and so on.
Any help would be entertained. Thanks in advance.