#include<stdio.h>
#include <iostream>
#include <math.h>
#include <numeric>
#include <vector>
#include <map>
#include <functional>
#include <stdio.h>
#include <array>
#include <algorithm>
#include <string>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <queue>
#include<iomanip>
#include<bitset>
#include<stack>
#include<set>
#include<limits>
#include <complex>
using namespace std;
template < class BidirectionalIterator >
bool next_combination(BidirectionalIterator first1,
	BidirectionalIterator last1,
	BidirectionalIterator first2,
	BidirectionalIterator last2)
{
	if ((first1 == last1) || (first2 == last2)) {
		return false;
	}
	BidirectionalIterator m1 = last1;
	BidirectionalIterator m2 = last2; --m2;
	while (--m1 != first1 && !(*m1 < *m2)) {
	}
	bool result = (m1 == first1) && !(*first1 < *m2);
	if (!result) {
		// ?
		while (first2 != m2 && !(*m1 < *first2)) {
			++first2;
		}
		first1 = m1;
		std::iter_swap(first1, first2);
		++first1;
		++first2;
	}
	if ((first1 != last1) && (first2 != last2)) {
		// ?
		m1 = last1; m2 = first2;
		while ((m1 != first1) && (m2 != last2)) {
			std::iter_swap(--m1, m2);
			++m2;
		}
		// ?
		std::reverse(first1, m1);
		std::reverse(first1, last1);
		std::reverse(m2, last2);
		std::reverse(first2, last2);
	}
	return !result;
}

template < class BidirectionalIterator >
bool next_combination(BidirectionalIterator first,
	BidirectionalIterator middle,
	BidirectionalIterator last)
{
	return next_combination(first, middle, middle, last);
}




#define _GLIBCXX_DEBUG
#include <iostream>
#include <vector>

using namespace std;

#define REP(i,n) for(int i=0;i<(int)n;++i)
#define FOR(i,c) for(auto i=(c).begin();i!=(c).end();++i)
#define ALL(c) (c).begin(), (c).end()

const int INF = 999999999;

long double times[100][100];
typedef int Weight;
struct Edge {
	int src, dst;
	Weight weight;
	Edge(int src, int dst, Weight weight) :
		src(src), dst(dst), weight(weight) { }
};
bool operator < (const Edge &e, const Edge &f) {
	return e.weight != f.weight ? e.weight > f.weight : // !!INVERSE!!
	e.src != f.src ? e.src < f.src : e.dst < f.dst;
}
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;

typedef vector<Weight> Array;
typedef vector<Array> Matrix;
#define RESIDUE(s,t) (capacity[s][t]-flow[s][t])
Weight maximumFlow(const Graph &g, int s, int t) {
	int n = g.size();
	Matrix flow(n, Array(n)), capacity(n, Array(n));
	REP(u, n) FOR(e, g[u]) capacity[e->src][e->dst] += e->weight;

	Weight total = 0;
	while (1) {
		queue<int> Q; Q.push(s);
		vector<int> prev(n, -1); prev[s] = s;
		while (!Q.empty() && prev[t] < 0) {
			int u = Q.front(); Q.pop();
			FOR(e, g[u]) if (prev[e->dst] < 0 && RESIDUE(u, e->dst) > 0) {
				prev[e->dst] = u;
				Q.push(e->dst);
			}
		}
		if (prev[t] < 0) return total; // prev[x] == -1 <=> t-side
		Weight inc = INF;
		for (int j = t; prev[j] != j; j = prev[j])
			inc = min(inc, RESIDUE(prev[j], j));
		for (int j = t; prev[j] != j; j = prev[j])
			flow[prev[j]][j] += inc, flow[j][prev[j]] -= inc;
		total += inc;
	}
}

struct tumiki {
	long long int x;
	long long int y;
	long long int id;
};

class Compare {
public:
	bool operator()(const pair<int,long long  int>&l, const pair<int, long long int >&r) {
		return l.second > r.second;
	}
};

int main() {
	while (1) {
		memset(times, 0, sizeof(times));
		int N, M; cin >> N >> M;
		if (!N)break;

		vector<complex<long double>>ts;
		vector<complex<long double>>bs;
		vector<long double>vs;
		for (int i = 0; i < N; ++i) {
			int x, y, v; cin >> x >> y >> v;
			ts.emplace_back(x, y);
			vs.emplace_back(v);
		}
		for (int i = 0; i < M; ++i) {
			int x, y; cin >> x >> y ;
			bs.emplace_back(x, y);
		}

		for (int i = 0; i < N; ++i) {
			for (int j = 0; j < M; ++j) {
				times[i][j] = abs(ts[i] - bs[j]) / vs[i];
			}
		}

		long double amin = 0;
		long double amax =4000000;
		while (amax - amin > 1e-9) {
			long double amid = (amax + amin) / 2;
			Graph g(2+N+M);
			for (int i = 0; i < N; ++i) {
				g[0].push_back(Edge(0, i + 1, 1));
			}
			for (int i = 0; i < M; ++i) {
				g[N + 1+i].push_back(Edge(N+1+i, M+N+1, 1));
			}
			for (int i = 0; i < N; ++i) {
				for (int j = 0; j < M; ++j) {
					if (times[i][j] < amid) {
						g[1+i].push_back(Edge(1+i, N + j+1, 1));
						g[N + j + 1].push_back(Edge(N + j + 1, 1+i, 0));
					}
					else {
					}
				}
			}
			if (maximumFlow(g, 0, M + N + 1) >= M) {
				amax = amid;
			}
			else {
				amin = amid;
			}
			
		}
		cout  <<fixed<<setprecision(33)<<amin << endl;
	}

	return 0;
	long long int N; cin >> N;
	vector<bool>used(N, false);
	vector<tumiki>tus;

	for (int i = 0; i < N; ++i) {
		int a, b; cin >> a >> b;
		tus.push_back(tumiki{a, b, i});
		tus.push_back(tumiki{ b, a, i });
	}
	sort(tus.begin(), tus.end(), [](const tumiki&l, const tumiki&r) {
		return l.x == r.x ? l.y > r.y : l.x < r.x; });

	vector<vector<pair<int, long long int>>>edges(2*N+2);
	for (int i = 0; i <2* N; ++i) {
		edges[0].push_back(make_pair(i + 1, tus[i].y));
		edges[i+1].push_back(make_pair(2*N+1, 0));
	}
	for (int i = 0; i < 2 * N; ++i) {
		for (int j = 0; j < 2 * N; ++j) {
			if (tus[i].id == tus[j].id) {
				continue;
			}
			else {
				if (tus[i].x < tus[j].x) {
					edges[i + 1].push_back(make_pair(j+1, tus[j].y));
				}
			}
		}
	}

	vector<int>ans(2 * N + 2,-1);

	priority_queue<pair<int, long long int>,vector<pair<int,long long int >>,Compare>que;
	que.push({ 0,0 });
	while (!que.empty()) {
		pair<int,long long int >atop(que.top());
		que.pop();
		if (ans[atop.first] < atop.second) {
			for (auto i : edges[atop.first]) {
				if (i.second + atop.second > ans[i.first]) {
					que.push({ i.first,ans[i.first] + atop.second });
					ans[i.first] = i.second + atop.second;
				}
			}
		}
	}
	cout << ans[2 * N + 1] << endl;
	



	return 0;

}

Measured memory_bytes: 4040